/*
 * $Id:   Broadcom SDK $
 * 
 * $Copyright: Copyright 2011 Broadcom Corporation.
 * This program is the proprietary software of Broadcom Corporation
 * and/or its licensors, and may only be used, duplicated, modified
 * or distributed pursuant to the terms and conditions of a separate,
 * written license agreement executed between you and Broadcom
 * (an "Authorized License").  Except as set forth in an Authorized
 * License, Broadcom grants no license (express or implied), right
 * to use, or waiver of any kind with respect to the Software, and
 * Broadcom expressly reserves all rights in and to the Software
 * and all intellectual property rights therein.  IF YOU HAVE
 * NO AUTHORIZED LICENSE, THEN YOU HAVE NO RIGHT TO USE THIS SOFTWARE
 * IN ANY WAY, AND SHOULD IMMEDIATELY NOTIFY BROADCOM AND DISCONTINUE
 * ALL USE OF THE SOFTWARE.  
 *  
 * Except as expressly set forth in the Authorized License,
 *  
 * 1.     This program, including its structure, sequence and organization,
 * constitutes the valuable trade secrets of Broadcom, and you shall use
 * all reasonable efforts to protect the confidentiality thereof,
 * and to use this information only in connection with your use of
 * Broadcom integrated circuit products.
 *  
 * 2.     TO THE MAXIMUM EXTENT PERMITTED BY LAW, THE SOFTWARE IS
 * PROVIDED "AS IS" AND WITH ALL FAULTS AND BROADCOM MAKES NO PROMISES,
 * REPRESENTATIONS OR WARRANTIES, EITHER EXPRESS, IMPLIED, STATUTORY,
 * OR OTHERWISE, WITH RESPECT TO THE SOFTWARE.  BROADCOM SPECIFICALLY
 * DISCLAIMS ANY AND ALL IMPLIED WARRANTIES OF TITLE, MERCHANTABILITY,
 * NONINFRINGEMENT, FITNESS FOR A PARTICULAR PURPOSE, LACK OF VIRUSES,
 * ACCURACY OR COMPLETENESS, QUIET ENJOYMENT, QUIET POSSESSION OR
 * CORRESPONDENCE TO DESCRIPTION. YOU ASSUME THE ENTIRE RISK ARISING
 * OUT OF USE OR PERFORMANCE OF THE SOFTWARE.
 * 
 * 3.     TO THE MAXIMUM EXTENT PERMITTED BY LAW, IN NO EVENT SHALL
 * BROADCOM OR ITS LICENSORS BE LIABLE FOR (i) CONSEQUENTIAL,
 * INCIDENTAL, SPECIAL, INDIRECT, OR EXEMPLARY DAMAGES WHATSOEVER
 * ARISING OUT OF OR IN ANY WAY RELATING TO YOUR USE OF OR INABILITY
 * TO USE THE SOFTWARE EVEN IF BROADCOM HAS BEEN ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGES; OR (ii) ANY AMOUNT IN EXCESS OF
 * THE AMOUNT ACTUALLY PAID FOR THE SOFTWARE ITSELF OR USD 1.00,
 * WHICHEVER IS GREATER. THESE LIMITATIONS SHALL APPLY NOTWITHSTANDING
 * ANY FAILURE OF ESSENTIAL PURPOSE OF ANY LIMITED REMEDY.$
 */

#ifdef INCLUDE_CES
#include <soc/defs.h>
#include <soc/drv.h>
#include <soc/debug.h>
#include <sal/core/libc.h>
#include <bcm/error.h>
/*#define BCM_CES_CRM_SUPPORT 1 */
#include <bcm_int/esw/ces.h>
#include <pub/nd_api.h>
#include <pub/cls_mp_handler.h>
#include <shared/alloc.h>
#include <soc/shared/mos_msg_common.h>
#include <soc/uc_msg.h>
#include <sal/appl/sal.h>
#include <bcm/types.h>
#include <shared/pbmp.h>
#include <bcm_int/esw/link.h>
#undef BCM_HIDE_DISPATCHABLE
#include <bcm/link.h>
#include <bcm/port.h>
#include <soc/dport.h>
#ifdef BCM_WARM_BOOT_SUPPORT
#include <bcm/module.h>
#include <bcm_int/esw/switch.h>
#endif /* BCM_WARM_BOOT_SUPPORT */

#define BCM_CES_IPV4_TOS     0
#define BCM_CES_IPV4_TTL     10
#define BCM_CES_IP_VERSION   4
#define BCM_CES_UDP_DESTINATION 0xef00
#define BCM_CES_UDP_SOURCE 0xef00
#define BCM_CES_VLAN_COUNT 1
#define BCM_CES_MPLS_COUNT 0


/*
 * Prototypes
 */
int bcm_esw_ces_service_destroy_all(int unit);
int bcm_esw_ces_service_enable_set(int unit, 
				   bcm_ces_service_t ces_service, 
				   int enable);
int bcm_esw_ces_service_free_tdm(int unit, 
				 bcm_ces_service_t ces_service);
int bcm_esw_ces_services_cclk_config_set(int unit, 
					 bcm_ces_cclk_config_t *config);

#ifdef INCLUDE_BCM_API_XLATE_PORT
#define XLATE_P2A(_u,_p) BCM_API_XLATE_PORT_P2A(_u,&_p) == 0
#else
#define XLATE_P2A(_u,_p) 1
#endif

#define DPORT_BCM_PBMP_ITER(_u,_pbm,_dport,_p) \
        for ((_dport) = 0, (_p) = -1; (_dport) < SOC_DPORT_MAX; (_dport)++) \
                if (((_p) = soc_dport_to_port(_u,_dport)) >= 0 && \
                    XLATE_P2A(_u,_p) && SOC_PBMP_MEMBER((_pbm),(_p)))

/**
 * Function:
 *      bcm_ces_service_config_t_init()
 * Purpose:
 *      Initialize service control structure
 * Parameters:
 * Returns:
 *      Result
 * Notes:
 */
void bcm_ces_service_config_t_init(bcm_ces_service_config_t *config) {

    if (config != NULL) {
        sal_memset(config, 0, sizeof(bcm_ces_service_config_t));
    }
    return;
}

/**
 * Function:
 *      bcm_ces_cclk_config_t_init()
 * Purpose:
 *      Initialize common clock control structure
 * Parameters:
 * Returns:
 *      Result
 * Notes:
 */
void bcm_ces_cclk_config_t_init(bcm_ces_cclk_config_t *config) {

    if (config != NULL) {
        sal_memset(config, 0, sizeof(bcm_ces_cclk_config_t));
    }
    return;
}


/**
 * Function:
 *      bcm_ces_rclock_config_t_init()
 * Purpose:
 *      Initialize rclock control structure
 * Parameters:
 * Returns:
 *      Result
 * Notes:
 */
void bcm_ces_rclock_config_t_init(bcm_ces_rclock_config_t *config) {

    if (config != NULL) {
        sal_memset(config, 0, sizeof(bcm_ces_rclock_config_t));
    }
    return;
}


/**
 * Function:
 *      bcm_esw_ces_enable_mii_port()
 * Purpose:
 *      Used to switch ge0 to CES mode
 * Parameters:
 * Returns:
 *      Result
 * Notes:
 */
#define CES_LINK 1
int bcm_esw_ces_enable_mii_port(int unit, uint32 mii_port) {
    extern int bcm_esw_port_speed_set(int,bcm_port_t,int);
#ifdef CES_LINK
extern int bcm_port_config_get(
    int unit, 
    bcm_port_config_t *config);
    pbmp_t      pbm_sw, pbm_hw, pbm_none, pbm_force;
    pbmp_t	pbm_temp;
    pbmp_t	pbm_sw_pre,pbm_hw_pre,pbm_none_pre;
    int us, rv;
    bcm_port_config_t   pcfg;
    soc_port_t		port, dport;

    if (bcm_port_config_get(unit, &pcfg) != BCM_E_NONE) {
	return BCM_E_INTERNAL;
    }

    /*
     * First get current linkscan state.  (us == 0 if disabled).
     */

    if ((rv = bcm_linkscan_enable_get(unit, &us)) < 0) {
	return BCM_E_INTERNAL;
    }

    /*
     * Get linkscan port bitmap
     */
    BCM_PBMP_CLEAR(pbm_sw);
    BCM_PBMP_CLEAR(pbm_hw);
    BCM_PBMP_CLEAR(pbm_force);

    DPORT_BCM_PBMP_ITER(unit, pcfg.port, dport, port) {
	int		mode;

	if ((rv = bcm_linkscan_mode_get(unit, port, &mode)) < 0) {
	    soc_cm_print("failed to get mode for port %d\n", port);
	} else {
	    switch (mode) {
	    case BCM_LINKSCAN_MODE_SW:
		BCM_PBMP_PORT_ADD(pbm_sw, port);
		break;
	    case BCM_LINKSCAN_MODE_HW:
		BCM_PBMP_PORT_ADD(pbm_hw, port);
		break;
	    default:
		break;
	    }
	}
    }

    BCM_PBMP_ASSIGN(pbm_sw_pre, pbm_sw);
    BCM_PBMP_ASSIGN(pbm_hw_pre, pbm_hw);
    BCM_PBMP_REMOVE(pbm_sw_pre, PBMP_HG_SUBPORT_ALL(unit));
    BCM_PBMP_REMOVE(pbm_hw_pre, PBMP_HG_SUBPORT_ALL(unit));

    /*
     * Remove ge0 from the bitmap
     */
    BCM_PBMP_CLEAR(pbm_temp);
    BCM_PBMP_PORT_SET(pbm_temp, mii_port);
    BCM_PBMP_REMOVE(pbm_sw, pbm_temp);

    /*
     * Turn off linkscan
     */
    if ((rv = bcm_linkscan_enable_set(unit, 0)) < 0) {
	return BCM_E_INTERNAL;
    }

    /*
     * Force link up
     */
    _bcm_esw_link_force(unit, mii_port, TRUE, TRUE); 

    /*
     *
     */
    BCM_PBMP_AND(pbm_sw, pcfg.port);
    BCM_PBMP_AND(pbm_hw, pcfg.port);
    BCM_PBMP_ASSIGN(pbm_none, pcfg.port);
    BCM_PBMP_REMOVE(pbm_sw, PBMP_HG_SUBPORT_ALL(unit));
    BCM_PBMP_REMOVE(pbm_hw, PBMP_HG_SUBPORT_ALL(unit));
    BCM_PBMP_REMOVE(pbm_none, PBMP_HG_SUBPORT_ALL(unit));

    BCM_PBMP_ASSIGN(pbm_temp, pbm_sw);
    BCM_PBMP_OR(pbm_temp, pbm_hw);
    BCM_PBMP_XOR(pbm_none, pbm_temp);
    BCM_PBMP_AND(pbm_force, pcfg.port);

    BCM_PBMP_ASSIGN(pbm_temp, pbm_sw);
    BCM_PBMP_AND(pbm_temp, pbm_hw);

    if ((rv = bcm_linkscan_mode_set_pbm(unit, pbm_sw,
					BCM_LINKSCAN_MODE_SW)) < 0) {
	return BCM_E_INTERNAL;
    }


    if ((rv = bcm_linkscan_mode_set_pbm(unit, pbm_hw,
					BCM_LINKSCAN_MODE_HW)) < 0) {
	return BCM_E_INTERNAL;
    }

    /* Only set the port to mode_none state if it is not previously in this mode */
    BCM_PBMP_ASSIGN(pbm_none_pre, pcfg.port);
    BCM_PBMP_ASSIGN(pbm_temp, pbm_sw_pre);
    BCM_PBMP_OR(pbm_temp, pbm_hw_pre);
    BCM_PBMP_XOR(pbm_none_pre, pbm_temp);
    BCM_PBMP_XOR(pbm_none_pre, pbm_none); 
    BCM_PBMP_AND(pbm_none, pbm_none_pre);  /* the one changed from 0 to 1 */
    
    if ((rv = bcm_linkscan_mode_set_pbm(unit, pbm_none,   
                                          BCM_LINKSCAN_MODE_NONE)) < 0) {   
	return BCM_E_INTERNAL;
    }   

    /*
     * Turn on linkscan
     */
    if ((rv = bcm_linkscan_enable_set(unit, us)) < 0) {
	return BCM_E_INTERNAL;
    }
  
#endif

    
    /*
     * The CES MII can only link with GE0. This is acheived by setting the speed
     * of GE0 to 100MBps
     */
return bcm_esw_port_speed_set(unit, mii_port, 100);
}


#ifdef BCM_CES_CRM_SUPPORT 
/**
 * Function:
 *      bcm_esw_ces_crm_msg_send
 * Purpose:
 *      Send message to CRM
 * Parameters:
 * Returns:
 *      Result
 * Notes:
 */
 int bcm_esw_ces_crm_dma_msg_send(int unit, uint8 msg_type, uint8 *data, int size) {
    mos_msg_data_t msg;         /* 64-bit message */
    mos_msg_data_t reply;       /* 64-bit reply */
    void *buffer;
    int ret = BCM_E_NONE;
    int res;
    bcm_ces_service_global_config_t *ces_ctrl = (bcm_ces_service_global_config_t *)SOC_CONTROL(unit)->ces_ctrl;

    /*
     * Build the message
     */
    msg.s.mclass    = MOS_MSG_CLASS_CES; /* CES message class */
    msg.s.subclass = msg_type;          /* CES subclass */

    /* 
     * The contents of this message will be a buffer pointer 
     */
    buffer = soc_cm_salloc(unit, size, "CES Dma-able message buffer");
    
    if (buffer == NULL) {
	ret =  BCM_E_MEMORY;
    } else {

	sal_memcpy(buffer, data, size);
	soc_cm_sflush(unit, buffer, size); /* Flush cache */

	msg.s.len = size;
	msg.s.data = soc_cm_l2p(unit, buffer);  /* Physical addr!! */

	/* 
	 * Send the msg then wait up to 1 sec for a reply 
	 */
	res = soc_cmic_uc_msg_send_receive(unit, ces_ctrl->crm_core, &msg, &reply, 1000000);
	soc_cm_sfree(unit, buffer);

	if (res != SOC_E_NONE ||
	    reply.s.mclass != MOS_MSG_CLASS_CES ||
	    reply.s.subclass != MOS_MSG_SUBCLASS_CES_CRM_CONFIG_REPLY) 
	    ret = BCM_E_INTERNAL;
    }

    return ret;
}


/**
 * Function:
 *      bcm_esw_ces_crm_init
 * Purpose:
 *      Identify CRM location and say hello
 * Parameters:
 * Returns:
 *      Result
 * Notes:
 */
static int bcm_esw_ces_crm_init(int unit, uint8 flags) {
    bcm_ces_crm_init_msg_t *init_msg;
    mos_msg_data_t uc_msg, uc_reply;
    int c, found=0;
    int result = BCM_E_NONE;
    bcm_ces_service_global_config_t *ces_ctrl = (bcm_ces_service_global_config_t *)SOC_CONTROL(unit)->ces_ctrl;

    /*
     * Although this message is small I'm using the DMA transfer
     * so that if the message grows there will be little to change.
     */
    init_msg = soc_cm_salloc(unit, sizeof(bcm_ces_crm_init_msg_t), "CES Init");
    init_msg->flags = flags;

    soc_cm_sflush(unit, init_msg, sizeof(bcm_ces_crm_init_msg_t));

    /* send init msg to BTE */
    sal_memset(&uc_msg, 0, sizeof(uc_msg));
    sal_memset(&uc_reply, 0, sizeof(uc_reply));
    uc_msg.s.mclass   = MOS_MSG_CLASS_CES;
    uc_msg.s.subclass = MOS_MSG_SUBCLASS_CES_CRM_INIT;
    uc_msg.s.len = sizeof(bcm_ces_crm_init_msg_t);
    uc_msg.s.data = soc_cm_l2p(unit, init_msg);

    /* Determine which BTE is running CES.  Try uC to see if the
     * applications start there */
    for (c=0; c < _CES_NUM_CMICM; c++) {
        result = soc_cmic_uc_appl_init(unit, c, MOS_MSG_CLASS_CES, _CES_UC_MSG_TIMEOUT_USECS);
        if (SOC_E_NONE == result){
            /* BFD started successfully */
            soc_cm_print("RECEIVED CES APPL READY, result: 0x%x\n", result);
	    ces_ctrl->crm_core = c;
            found = 1;
            break;
        }
    }

    soc_cm_sfree(unit, init_msg);

    if (!found) {
        result BCM_E_INTERNAL;
    }

    return result;
}
#endif


/**
 * Function:
 *      bcm_esw_ces_pmi_cb
 * Purpose:
 *      CES PM counter callback. Called once per second
 * Parameters:
 * Returns:
 *      Result
 * Notes:
 */
AgResult bcm_esw_ces_pmi_cb(void) {
    int unit = 0; 
    AgResult n_ret;
    AgNdMsgPmIngress ndPmIngress;
    AgNdMsgPmEgress  ndPmEgress;
    bcm_ces_service_record_t *record;
    bcm_ces_service_global_config_t *ces_ctrl = (bcm_ces_service_global_config_t *)SOC_CONTROL(unit)->ces_ctrl;
    int i, j;

    /* SOC_DEBUG_PRINT((DK_VERBOSE,"%s:\n", __func__)); */
    if (!BCM_CES_IS_CONFIGURED(ces_ctrl))
	return AG_S_OK;

    /*
     * Loop through all service records.
     */
    for (i = 0;i < BCM_CES_CIRCUIT_IDX_MAX;i++) {
	if (ces_ctrl->bcm_ces_service_records[i] == NULL)
	    continue;

	record = ces_ctrl->bcm_ces_service_records[i];

	if (!BCM_CES_IS_ENABLED(&(record->config)))
	    continue;

	/*
	 * Ingress stats
	 */
	sal_memset(&ndPmIngress, 0, sizeof(ndPmIngress));
	ndPmIngress.n_channel_id = i;
	n_ret = ag_nd_device_read(ces_ctrl->ndHandle, AG_ND_OPCODE_PM_INGRESS, &ndPmIngress);
	if (!AG_SUCCEEDED(n_ret))
	{
	    return AG_E_FAIL;
	}

	record->pm_counts.transmitted_bytes += ndPmIngress.n_tpe_pwe_out_byte_counter;
	record->pm_counts.transmitted_packets += ndPmIngress.n_tpe_pwe_out_packet_counter;


	/*
	 * Egress stats
	 */
	sal_memset(&ndPmEgress, 0, sizeof(ndPmEgress));
	ndPmEgress.n_channel_id = i;
	n_ret = ag_nd_device_read(ces_ctrl->ndHandle, AG_ND_OPCODE_PM_EGRESS, &ndPmEgress);
	if (!AG_SUCCEEDED(n_ret))
	{
	    return AG_E_FAIL;
	}

	if (record->pm_counts.jbf_depth_min == 0 ||
	    ndPmEgress.n_jbf_depth_min < record->pm_counts.jbf_depth_min)
	    record->pm_counts.jbf_depth_min =  ndPmEgress.n_jbf_depth_min;

	if (ndPmEgress.n_jbf_depth_max > record->pm_counts.jbf_depth_max)
	    record->pm_counts.jbf_depth_max =  ndPmEgress.n_jbf_depth_max;

	record->pm_counts.jbf_underruns += ndPmEgress.n_jbf_underruns;
	record->pm_counts.jbf_missing_packets  += ndPmEgress.n_jbf_missing_packets;
	record->pm_counts.jbf_dropped_ooo_packets += ndPmEgress.n_jbf_dropped_ooo_packets;
	record->pm_counts.jbf_reordered_ooo_packets += ndPmEgress.n_jbf_reordered_ooo_packets;
	record->pm_counts.jbf_bad_length_packets += ndPmEgress.n_jbf_bad_length_packets;
	record->pm_counts.received_bytes     += ndPmEgress.n_rpc_pwe_in_bytes;
	record->pm_counts.received_packets   += ndPmEgress.n_rpc_pwe_in_packets;

	for (j = 0;j < AG_ND_RPC_CCNT_MAX;j++) {
	    record->pm_counts.rpc_channel_specific[j] += ndPmEgress.a_rpc_channel_specific[j];
	}
    }

    return AG_S_OK;
}



/**
 * Function:
 *      bcm_esw_ces_pbi_cb
 * Purpose:
 *      CES PBF overflow callback
 * Parameters:
 *      n_channel_id - (IN) Channel ID
 *      n_user_data  - (IN) User data
 * Returns:
 *      Result
 * Notes:
 */
AgResult bcm_esw_ces_pbi_cb(AG_U32 n_channel_id, AG_U32 n_user_data) {
    int unit = (int)n_user_data; 
    bcm_ces_service_global_config_t *ces_ctrl = (bcm_ces_service_global_config_t *)SOC_CONTROL(unit)->ces_ctrl;

    SOC_DEBUG_PRINT((DK_WARN,"%s: PBF overflow on service:%lu\n", 
		     __func__,
		     n_channel_id));
    if (!BCM_CES_IS_CONFIGURED(ces_ctrl))
	return AG_S_OK;

    return AG_S_OK;
}

/**
 * Function:
 *      bcm_esw_ces_cwi_cb
 * Purpose:
 *      CES control word change callback
 * Parameters:
 *      n_channel_id - (IN) Channel ID
 *      n_user_data  - (IN) User data
 *      n_cw         - (IN) Control word
 * Returns:
 *      Result
 * Notes:
 */
AgResult bcm_esw_ces_cwi_cb(AG_U32 n_channel_id, AG_U32 n_user_data, AG_U16 n_cw) {
    int unit = (int)n_user_data; 
    bcm_ces_service_global_config_t *ces_ctrl = (bcm_ces_service_global_config_t *)SOC_CONTROL(unit)->ces_ctrl;

    SOC_DEBUG_PRINT((DK_VERBOSE,"%s: Service:%lu CES control word bits changed to:0x%04x\n", 
		     __func__,
		     n_channel_id,
		     n_cw));
    if (!BCM_CES_IS_CONFIGURED(ces_ctrl))
	return AG_S_OK;

    return AG_S_OK;
}

/**
 * Function:
 *      bcm_esw_ces_psi_cb
 * Purpose:
 *      CES Packet sync status change
 * Parameters:
 *      n_channel_if - (IN) Channel ID
 *      n_user_data  - (IN) User data
 *      n_sync       - (IN) Current sync state
 * Returns:
 *      Result
 * Notes:
 */
AgResult bcm_esw_ces_psi_cb(AG_U32 n_channel_id, AG_U32 n_user_data, AgNdSyncType n_sync) {
    int unit = (int)n_user_data; 
    bcm_ces_service_global_config_t *ces_ctrl = (bcm_ces_service_global_config_t *)SOC_CONTROL(unit)->ces_ctrl;

    SOC_DEBUG_PRINT((DK_VERBOSE,"%s: Service:%lu sync status changed to:%s\n", 
		     __func__,
		     n_channel_id,
		     (n_sync == 0 ? "LOPS":"AOPS")));
    if (!BCM_CES_IS_CONFIGURED(ces_ctrl))
	return AG_S_OK;

    return AG_S_OK;
}

/**
 * Function:
 *      bcm_ces_port_to_circuit_id
 * Purpose:
 *      Converts platform port number to zero based circuit_id
 * Parameters:
 *      unit - (IN) Unit number.
 *      port
 * Returns:
 *      circuit id
 * Notes:
 */
int bcm_ces_port_to_circuit_id(int unit, bcm_port_t port) {
    if (SOC_IS_KATANA(unit))
	return (port - BCM_CES_TDM_PORT_BASE_KATANA);
    else
	return port;
}

/**
 * Function:
 *      bcm_ces_circuit_id_to_port
 * Purpose:
 *      Converts zero based circuit_id to platform port number
 * Parameters:
 *      unit - (IN) Unit number.
 *      port
 * Returns:
 *      circuit id
 * Notes:
 */
bcm_port_t bcm_ces_circuit_id_to_port(int unit, int circuit_id) {
    if (SOC_IS_KATANA(unit))
	return (circuit_id + BCM_CES_TDM_PORT_BASE_KATANA);
    else
	return circuit_id;
}

/**
 * Function:
 *      bcm_ces_service_get_service
 * Purpose:
 *      Find free channel
 * Parameters:
 *      unit - (IN) Unit number.
 *      ces_service
 * Returns:
 *      circuit id
 * Notes:
 */
int bcm_ces_service_get_service(int unit, 
				bcm_ces_service_t *ces_service,
				bcm_ces_service_record_t **record,
                                int use_id) {
    int i;
    bcm_ces_service_global_config_t *ces_ctrl = (bcm_ces_service_global_config_t *)SOC_CONTROL(unit)->ces_ctrl;

    if (use_id == 0) {
	/*
	 * Find first unused circuit.
	 */
	for (i = 0;i < BCM_CES_CIRCUIT_IDX_MAX;i++) {
	    if (ces_ctrl->bcm_ces_service_records[i] == NULL) {
		return BCM_E_INTERNAL;
	    } else if (BCM_CES_IS_FREE(&ces_ctrl->bcm_ces_service_records[i]->config)) {
		*record = ces_ctrl->bcm_ces_service_records[i];

		/*
		 * Mark as used.
		 */
		BCM_CES_CLR_ALL(&(*record)->config);
		*ces_service = (*record)->ces_service;
		return BCM_E_NONE;
	    }
	}
    } else {
	if (ces_ctrl->bcm_ces_service_records[*ces_service] == NULL) {
	    return BCM_E_INTERNAL;
	} else if (BCM_CES_IS_FREE(&ces_ctrl->bcm_ces_service_records[*ces_service]->config)) {
	    *record = ces_ctrl->bcm_ces_service_records[*ces_service];

	    /*
	     * Mark as used.
	     */
	    BCM_CES_CLR_ALL(&(*record)->config);
	    return BCM_E_NONE;
	} else {
	    return BCM_E_UNAVAIL;
	}
    }

    /*
     * All services taken
     */
    return BCM_E_FULL;
}

/**
 * Function:
 *      bcm_ces_service_init_record
 * Purpose:
 *      Initialize service record with default values
 * Parameters:
 *      unit - (IN) Unit number.
 *      ces_service
 *      record - (IN) allocated record
 * Returns:
 *      BCM_E_
 * Notes:
 */
int bcm_ces_service_init_record(int unit, 
				bcm_ces_service_t ces_service)
{
    bcm_ces_service_record_t *record;
    int n_ret;
    bcm_ces_service_global_config_t *ces_ctrl = (bcm_ces_service_global_config_t *)SOC_CONTROL(unit)->ces_ctrl;
    int i;


    /*
     * Find service record.
     */
    n_ret = bcm_ces_service_find(unit, ces_service, &record);
    if (n_ret != BCM_E_NONE)
	return n_ret;

    if (record == NULL)
	return BCM_E_MEMORY;

    /*
     * Mark service record as free
     */
    sal_memset(record, 0, sizeof(bcm_ces_service_record_t));
    BCM_CES_CLR_ALL(&record->config);
    BCM_CES_SET_FREE(&record->config);

    record->ces_service = ces_service;

    /*
     * Set default values
     *
     * Channel maps are initially empty so there's nothing to
     * do there.
     */

    /*
     * Ingress channel config
     */
    if (ces_ctrl->protocol == bcmCesTdmProtocolE1)
	record->config.ingress_channel_config.payload_size = BCM_CES_E1_PAYLOAD_SIZE;
    else
	record->config.ingress_channel_config.payload_size = BCM_CES_T1_PAYLOAD_SIZE;

    record->config.ingress_channel_config.dba = FALSE;
    record->config.ingress_channel_config.auto_r_bit = 0;
    record->config.ingress_channel_config.mef_len_support = 0;
    record->config.ingress_channel_config.pbf_size = BCM_CES_PACKET_BUFFER_SIZE;

    /*
     * Egress channel config
     */
    record->config.egress_channel_config.packet_sync_selector = 0;
    record->config.egress_channel_config.rtp_exists = FALSE;
    if (ces_ctrl->protocol == bcmCesTdmProtocolE1)
	record->config.egress_channel_config.payload_size = BCM_CES_E1_PAYLOAD_SIZE;
    else
	record->config.egress_channel_config.payload_size = BCM_CES_T1_PAYLOAD_SIZE;
    record->config.egress_channel_config.jbf_ring_size = BCM_CES_JBF_RING_SIZE;
    record->config.egress_channel_config.jbf_win_size = BCM_CES_JBF_WINDOW_SIZE;
    record->config.egress_channel_config.jbf_bop = BCM_CES_JBF_BREAK_OUT_POINT;

    /*
     * Header
     */
    sal_memcpy(record->config.header.eth.source, ces_ctrl->mac, sizeof(sal_mac_addr_t));
    sal_memcpy(record->config.header.eth.destination, ces_ctrl->mac, sizeof(sal_mac_addr_t));

    record->config.header.encapsulation = bcmCesEncapsulationEth;
    record->config.header.ipv4.tos = BCM_CES_IPV4_TOS;
    record->config.header.ipv4.ttl = BCM_CES_IPV4_TTL;
    record->config.header.ipv4.source      = ces_ctrl->ipv4_addr;
    record->config.header.ipv4.destination = ces_ctrl->ipv4_addr;
    record->config.header.vc_label = 0xab00 + ces_service;
    record->config.header.udp.destination = BCM_CES_UDP_DESTINATION; 
    record->config.header.udp.source = BCM_CES_UDP_SOURCE;
    record->config.header.ip_version = BCM_CES_IP_VERSION;
    record->config.header.vlan_count = BCM_CES_VLAN_COUNT;
    record->config.header.vlan[0].priority = 1;
    record->config.header.vlan[0].vid =0x05;
    record->config.header.mpls_count = BCM_CES_MPLS_COUNT;
    record->config.header.rtp_exists = FALSE;
    record->config.header.udp_chksum = 0;

    /*
     * Strict data for ETH encap
     */
    record->config.strict_data.encapsulation = record->config.header.encapsulation;
    sal_memset(&record->config.strict_data, 0, sizeof(bcm_ces_packet_header_t));
    memcpy(record->config.strict_data.eth.destination, record->config.header.eth.source, 
           sizeof(sal_mac_addr_t));

    record->config.strict_data.vlan_count = record->config.header.vlan_count;

    for (i = 0;i < record->config.header.vlan_count;i++) {
	record->config.strict_data.vlan[i].vid = record->config.header.vlan[i].vid;
    }


    /*
     * Config
     */
    record->config.encapsulation = bcmCesEncapsulationEth;

    return BCM_E_NONE;
}


/**
 * Function:
 *      bcm_ces_service_allocate_record
 * Purpose:
 *      Allocate memory for service record
 * Parameters:
 *      unit - (IN) Unit number.
 *      ces_service
 *      record - (OUT) allocated record
 * Returns:
 *      BCM_E_
 * Notes:
 */
int bcm_ces_service_allocate_record(int unit, 
				    bcm_ces_service_t ces_service)
{
    bcm_ces_service_global_config_t *ces_ctrl = (bcm_ces_service_global_config_t *)SOC_CONTROL(unit)->ces_ctrl;
    bcm_ces_service_record_t *record;

    record = (bcm_ces_service_record_t*)sal_alloc(sizeof(bcm_ces_service_record_t),"CES Service record");
    ces_ctrl->bcm_ces_service_records[ces_service] = record;
    
    if (record == NULL)
	return BCM_E_MEMORY;

    /*
     * Init record
     */
    bcm_ces_service_init_record(unit, ces_service);

    return BCM_E_NONE;
}


 
/**
 * Function:
 *      bcm_ces_service_free_record
 * Purpose:
 *      Free memory for circuit record
 * Parameters:
 *      unit - (IN) Unit number.
 *      ces_service
 *
 * Returns:
 *      BCM_E_
 * Notes:
 */
int bcm_ces_service_free_record(int unit, 
				bcm_ces_service_t ces_service)
{
    bcm_ces_service_global_config_t *ces_ctrl = (bcm_ces_service_global_config_t *)SOC_CONTROL(unit)->ces_ctrl;
    bcm_ces_service_record_t *record = ces_ctrl->bcm_ces_service_records[ces_service];

    if (record != NULL)
    {
	/*
	 * Disable
	 */
	if (BCM_CES_IS_ENABLED(&record->config)) {
	    bcm_esw_ces_service_enable_set(unit, ces_service, 0);
	}

	/*
	 * Free the TDM resources
	 */
	bcm_esw_ces_service_free_tdm(unit, ces_service);

	/*
	 * Mark service as free
	 */
	BCM_CES_CLR_ALL(&record->config);
	BCM_CES_SET_FREE(&record->config);
	return BCM_E_NONE;
    }

    return BCM_E_INTERNAL;
}

/**
 * Function:
 *      bcm_ces_service_find
 * Purpose:
 *      Find service record form service id
 * Parameters:
 *      unit - (IN) Unit number.
 *      ces_service
 *
 * Returns:
 *      BCM_E_
 * Notes:
 */
int bcm_ces_service_find(int unit, 
			 bcm_ces_service_t ces_service,
			 bcm_ces_service_record_t **record)
{
    bcm_ces_service_global_config_t *ces_ctrl = (bcm_ces_service_global_config_t *)SOC_CONTROL(unit)->ces_ctrl;

    /*
     * Check that service number is valid
     */
    if (!bcm_ces_service_is_valid(ces_service)) {
	*record = NULL;
	return BCM_E_BADID;
    }

    /*
     * find service
     */
    if (ces_ctrl->bcm_ces_service_records[ces_service] != NULL) {
	*record = ces_ctrl->bcm_ces_service_records[ces_service]; 
	return BCM_E_NONE;
    }

    *record = NULL;
    return BCM_E_INTERNAL;
}


#define sal_tolower(c) ((c >= 'A' && c <= 'Z') ? (c + 37):c)
int  sal_isxdigit(char c) {
    if ((c >= '0' && c <= '9') || 
	(c >= 'a' && c <= 'f') ||
	(c >= 'A' && c <= 'F'))
	return 1; 
    return 0;
}

int sal_xdigit2i(char c) {
    if (c >= '0' && c <= '9')
	return (int)(c - '0');
    else if (c >= 'a' && c <= 'f')
	return (int)(c - 'a') + 10;
    else if (c >= 'A' && c <= 'F')
	return (int)(c - 'A') + 10;
    return 0;
}

char *sal_strchr(const char *str, int c) {
    const char *ptr = str;

    while(*ptr != '\0') {
	if (*ptr == (char)c)
	    return (char*)ptr;
	ptr++;
    }

    return NULL;
}

/*
 * Return true if a constant is a well-formed integer of the type
 * supported by parse_integer.
 */

int
sal_isint(char *s)
{
    int base;

    if (s == NULL) {
        return 0;
    }

    if (*s == '-') {
        s++;
    }

    if (*s == '0') {
        if (s[1] == 'b' || s[1] == 'B') {
            base = 2;
            s += 2;
        } else if (s[1] == 'x' || s[1] == 'X') {
            base = 16;
            s += 2;
        } else
            base = 8;
    } else {
        base = 10;
    }

    do {
        if (!sal_isxdigit((unsigned) *s) || sal_xdigit2i(*s) >= base) {
            return(0);
        }
    } while (*++s);

    return(1);
}

int bcm_ces_parse_macaddr(char *str, uint8 *macaddr)
{
    char *s;
    int	colon = FALSE;
    int	i, c1, c2;

    if (sal_strchr(str, ':')) {		/* Colon format */
	colon = TRUE;
    } else if (*str++ != '0' || sal_tolower(*str++) != 'x') {
	return -1;
    } else {
	sal_memset(macaddr, 0, 6);
    }
    /* Start at end and work back */
    s = str + sal_strlen(str);
    for (i = 5; (i >= 0) && (s >= str); i--) {
	c2 = (s > str && sal_isxdigit((unsigned) s[-1])) ? sal_xdigit2i((unsigned) *--s) : 0;
	c1 = (s > str && sal_isxdigit((unsigned) s[-1])) ? sal_xdigit2i((unsigned) *--s) : 0;
	macaddr[i] = c1 * 16 + c2;
	if (colon && (s >= str) && (':' != *--s))
	    break;
    }
    return(((s <= str) && (!colon || (i == 0))) ? 0 : -1);
}

int
bcm_ces_parse_ipaddr(char *s, ip_addr_t *ipaddr)
{
    char *ts;
    int i, x;
    ip_addr_t ip = 0;

    if (strchr(s, '.')) {               /* dotted notation */
        for (i = 0; i < 4; i++) {
            x = sal_ctoi(s, &ts);
            if ((x > 0xff) || (x < 0)) {
                return(-1);
            } 
            ip = (ip << 8) | x;
            if (*ts != '.') {   /* End of string */
                break;
            }
            s = ts + 1;
        }
        if (((i != 3) || (*ts != '\0'))) {
            return(-1);
        } else {
            *ipaddr = ip;
            return(0);
        }
    } else if (sal_isint(s)){
        *ipaddr = _shr_ctoi(s);
        return(0);
    } else {
        return(-1);
    }
}

#ifdef BCM_WARM_BOOT_SUPPORT

#define BCM_WB_VERSION_1_0                SOC_SCACHE_VERSION(1,0)
#define BCM_WB_DEFAULT_VERSION            BCM_WB_VERSION_1_0

/**
 * Function:
 *      bcm_esw_ces_warm_boot
 * Purpose:
 *      Warm boot CES module
 * Parameters:
 *      unit - (IN) Unit number.
 * Returns:
 *      BCM_E_NONE      Operation completed successfully
 *      BCM_E_UNAVAIL   The CES driver failed to initialize correctly
 *      BCM_E_MEMORY    Insufficient memory
 *      BCM_E_INTERNAL  An internal error occurred
 * Notes:
 *      Warm boots the CES device driver and allocates service control structures 
 */
int _bcm_esw_ces_reinit(int unit) {
    soc_scache_handle_t scache_handle;
    uint8 *ces_scache_ptr;

    SOC_SCACHE_HANDLE_SET(scache_handle, unit, BCM_MODULE_CES, 0);

    BCM_IF_ERROR_RETURN
        (_bcm_esw_scache_ptr_get(unit, scache_handle, FALSE,
                                 0, &ces_scache_ptr, 
                                 BCM_WB_DEFAULT_VERSION, NULL));

    /*
     * Call BATM init indicating warm boot and inform about valid services.
     */

    /*
     * Recover config from the device.
     */
    return BCM_E_NONE;
}

/**
 * Function:
 *      bcm_esw_ces_sync
 * Purpose:
 *      Sync warm boot data 
 * Parameters:
 *      unit - (IN) Unit number.
 * Returns:
 *      BCM_E_NONE      Operation completed successfully
 *      BCM_E_UNAVAIL   The CES driver failed to initialize correctly
 *      BCM_E_MEMORY    Insufficient memory
 *      BCM_E_INTERNAL  An internal error occurred
 * Notes:
 *      Sync method. Called automatically from switch code.
 */
int _bcm_esw_ces_sync(int unit) {
    soc_scache_handle_t scache_handle;
    uint8 *ces_scache_ptr;
    bcm_ces_service_global_config_t *ces_ctrl = (bcm_ces_service_global_config_t *)SOC_CONTROL(unit)->ces_ctrl;
    int i;
    bcm_ces_service_record_t *record;
    bcm_ces_warm_boot_t *wb_record;

    SOC_SCACHE_HANDLE_SET(scache_handle, unit, BCM_MODULE_CES, 0);

    BCM_IF_ERROR_RETURN
        (_bcm_esw_scache_ptr_get(unit, scache_handle, FALSE,
                                 0, &ces_scache_ptr, 
                                 BCM_WB_DEFAULT_VERSION, NULL));

    /*
     * Write configured CES services to warm boot cache
     */
    sal_memset(ces_scache_ptr, 0, sizeof(bcm_ces_warm_boot_t));
    wb_record = (bcm_ces_warm_boot_t*)ces_scache_ptr;

    for (i = 0;i < BCM_CES_CIRCUIT_IDX_MAX;i++) {
	if (ces_ctrl->bcm_ces_service_records[i] != NULL) {
	    record = ces_ctrl->bcm_ces_service_records[i]; 
	    wb_record->ces_service = record->ces_service;
	    wb_record++;
	}
    }

    return BCM_E_NONE;
}

#endif /* WARM_BOOT */


/**
 * Function:
 *      bcm_ces_init
 * Purpose:
 *      Initialize CES module
 * Parameters:
 *      unit - (IN) Unit number.
 * Returns:
 *      BCM_E_NONE      Operation completed successfully
 *      BCM_E_UNAVAIL   The CES driver failed to initialize correctly
 *      BCM_E_MEMORY    Insufficient memory
 *      BCM_E_INTERNAL  An internal error occurred
 * Notes:
 *      Initializes the CES device driver and allocates service control structures 
 */
int bcm_esw_ces_init(int unit)
{
    AgNdMsgOpen p_msg;
    uint32 addr;
    AgResult n_res;
    AgResult n_ret;
    bcm_ces_service_global_config_t **ces_ctrl = (bcm_ces_service_global_config_t **)&(SOC_CONTROL(unit)->ces_ctrl);
    bcm_ces_service_global_config_t *config;
    char *mac_str;
    char *ip_str;
    int ret;
    int i;
    uint32 coreid;
    char *str;
    bcm_ces_cclk_config_t cclk_config;
    uint32 mii_port;
#ifdef BCM_CES_CRM_SUPPORT    
    bcm_ces_crm_cclk_config_msg_t crm_config_msg;
#endif
#ifdef BCM_WARM_BOOT_SUPPORT
    soc_scache_handle_t scache_handle;
    uint32              ces_scache_size;
    uint8              *ces_scache_ptr;
#endif


    /*
     * Initialize CES16Core
     */
    if ((n_res = ag_nd_module_create()) != AG_S_OK)
    {
	if (n_res == AG_E_ND_MODULE_CREATED)
	    return BCM_E_NONE;

	return BCM_E_UNAVAIL;
    }

    /*
     * Get the MII port
     */
    mii_port = soc_property_get(unit, spn_CES_MII_PORT_NUMBER, 1);

    
    if (mii_port != 1) {
	mii_port = 1; 
    }


    /*
     * Check that CES is not already initialized
     */
    if (*ces_ctrl != NULL) {
	return BCM_E_NONE;
    }

    /*
     * Initialize control structs
     */
    *ces_ctrl = (bcm_ces_service_global_config_t*)sal_alloc(sizeof(bcm_ces_service_global_config_t),"CES Service");
    config = *ces_ctrl;

    if (*ces_ctrl == NULL)
	return BCM_E_MEMORY;

    sal_memset(*ces_ctrl, 0, sizeof(bcm_ces_service_global_config_t));
    config->ces_mii_port = mii_port;

    /* 
     * Find which core is running the CRM and say hello to the CRM
     */
#ifdef BCM_CES_CRM_SUPPORT    
    ret = bcm_esw_ces_crm_init(unit, BCM_CES_CRM_FLAG_INIT | (SOC_WARM_BOOT(unit) ? BCM_CES_CRM_FLAG_WB:0x00));
    if (ret != BCM_E_NONE) {
	SOC_DEBUG_PRINT((DK_ERR, "CES failed to locate CRM\n"));
	
	/* return ret; */
    }
#endif

    /*
     * Allocate and initialize service records.
     */
    for (i = 0;i < BCM_CES_CIRCUIT_IDX_MAX;i++)
    {
	if ((ret = bcm_ces_service_allocate_record(unit, i)) != BCM_E_NONE)
	{
	    return ret;
	}
    }

    /*
     * Base address
     */
    addr = soc_reg_addr(unit, COREIDr, REG_PORT_ANY, 0);
    SOC_DEBUG_PRINT((DK_VERBOSE, "CES base address:0x%08x\n", addr));


#ifdef PLISIM
    /*
     * Hack to set CES version in bcmsim
     */
    soc_reg32_write(unit, addr, BCM_CES_COREID);
#endif

    /*
     * Read the CES version. If this does not give the expected value
     * then we are done. (The BATM driver will not initialize correctly)
     */
    soc_reg32_read(unit, addr, &coreid);
    SOC_DEBUG_PRINT((DK_VERBOSE, "CES CORID:0x%04x\n", (uint16)coreid));

    if (coreid != BCM_CES_COREID) {
	SOC_DEBUG_PRINT((DK_VERBOSE, "CES CORID:0x%04x does not match expected value:0x%04x\n", 
			 (uint16)coreid, (uint16)BCM_CES_COREID));
	return BCM_E_INTERNAL;
    }

    /*
     * Open the CES device
     */
    sal_memset(&p_msg, 0, sizeof(AgNdMsgOpen));
    p_msg.n_base   = addr;
    p_msg.b_use_hw = AG_TRUE;
    p_msg.n_ext_mem_bank0_size = 0;
    p_msg.n_ext_mem_bank1_size = 0; 

    if (ag_nd_device_open(&p_msg, &config->ndHandle) != AG_S_OK)
    {
	ag_nd_module_remove();
	return BCM_E_UNAVAIL;
    }

#ifdef BCM_WARM_BOOT_SUPPORT    
    ces_scache_size = sizeof(bcm_ces_warm_boot_t) * BCM_CES_CIRCUIT_IDX_MAX; 

    SOC_SCACHE_HANDLE_SET(scache_handle, unit, BCM_MODULE_CES, 0);

    ret = _bcm_esw_scache_ptr_get(unit, scache_handle,
				  (0 == SOC_WARM_BOOT(unit)),
				  ces_scache_size, &ces_scache_ptr, 
				  BCM_WB_DEFAULT_VERSION, NULL);
    if (BCM_FAILURE(ret) && (ret != BCM_E_NOT_FOUND)) {
        return ret;
    }

    if (SOC_WARM_BOOT(unit)) {
        return _bcm_esw_ces_reinit(unit);
    }

#endif

    /*
     * Populate config with values from soc properties and default values.
     */
    config->ndInit.e_one_sec_pulse_direction = AG_ND_ONE_SECOND_PULSE_DIRECTION_OUT;
    config->ndInit.e_bit_order = AG_ND_TDM_PAYLOAD_BIT_ORDERING_STANDARD;

    config->ndInit.n_packet_max = 0;           /* Not used in ces16core */
    config->ndInit.n_pw_max = BCM_CES_PW_MAX;
    config->ndInit.n_pbf_max = 0;              /* Not used in ces16core */

    config->ndInit.b_isr_mode = AG_FALSE;       
    config->ndInit.n_isr_task_priority = 10;
    config->ndInit.n_isr_task_wakeup = 100;
    config->ndInit.b_ptp_support = AG_TRUE;
    config->ndInit.p_cb_pmi = bcm_esw_ces_pmi_cb;
    config->ndInit.p_cb_pbi = bcm_esw_ces_pbi_cb;
    config->ndInit.p_cb_cwi = bcm_esw_ces_cwi_cb;
    config->ndInit.p_cb_psi = bcm_esw_ces_psi_cb;
    config->ndInit.n_user_data_pbi = unit;
    config->ndInit.n_user_data_cwi = unit;
    config->ndInit.n_user_data_psi = unit;
    config->ndInit.n_user_data_tpi = unit;

    n_ret = ag_nd_device_write(config->ndHandle, AG_ND_OPCODE_CONFIG_INIT, &config->ndInit);
    if (!AG_SUCCEEDED(n_ret))
    {
	return BCM_E_INTERNAL;
    }


    /*
     * Setup default clock configuration. 
     *
     * - Framer clock enabled
     * - Reference Clock 1 selected
     * - T1/E1 selected from soc TDM_PROTO property (T1 default)
     * - Reference Clock 1 set to Internal 25MHz osc
     * 
     * With this configurtion all other parameters are ignored
     * hence they are not set here.
     */
    str = soc_property_get_str(unit, spn_CES_PORT_TDM_PROTO);
    if (str != NULL)
    {
	if ( sal_strcmp(str, "T1") == 0 )
	    config->protocol = bcmCesTdmProtocolT1;
	else if ( sal_strcmp(str, "E1") == 0 )
	    config->protocol = bcmCesTdmProtocolE1;
	else
	{
	    
	    return BCM_E_PARAM;
	}
    }
    else
	config->protocol = bcmCesTdmProtocolT1;

    /*
     * Clock config
     */
    

    sal_memset(&cclk_config, 0, sizeof(bcm_ces_cclk_config_t));
    cclk_config.cclk_enable      = TRUE;                           /* Enable clock to framer */
    cclk_config.cclk_select      = AG_ND_CCLK_SELECT_REF_CLK_1;    /* Common clock source select. */
    cclk_config.ref_clk_proto    = config->protocol;               /* Selects reference clock protocol. */
    cclk_config.ref_clk_1_select = AG_ND_REF_CLK_SELECT_NOMAD_BRG; /* Reference clock 1 source select */
    cclk_config.ref_clk_2_select = bcmCesCclkSelectRefClk1; /* Reference clock 1 source select */
    cclk_config.ref_clk_1_port   = bcm_ces_circuit_id_to_port(unit, 0);
    cclk_config.ref_clk_2_port   = bcm_ces_circuit_id_to_port(unit, 1);

    ret = bcm_esw_ces_services_cclk_config_set(unit, &cclk_config);
    if (ret != BCM_E_NONE) {
	
	return ret;
    }


    /*
     * Send config to CRM module
     */
#ifdef BCM_CES_CRM_SUPPORT    
    crm_config_msg.flags              = BCM_CES_CRM_FLAG_NONE;
    crm_config_msg.e_cclk_select      = cclk_config.cclk_select;
    crm_config_msg.e_ref_clk_1_select = cclk_config.ref_clk_1_select;
    crm_config_msg.e_ref_clk_2_select = cclk_config.ref_clk_2_select;
    crm_config_msg.n_ref_clk_1_port   = cclk_config.ref_clk_1_port;
    crm_config_msg.n_ref_clk_2_port   = cclk_config.ref_clk_2_port;

    n_ret =  bcm_esw_ces_crm_dma_msg_send(unit, MOS_MSG_SUBCLASS_CES_CRM_CONFIG, (uint8*)&crm_config_msg, sizeof(bcm_ces_crm_cclk_config_msg_t));
    if (n_ret != BCM_E_NONE)
    {

/*	return ret; */
    }
#endif


    /*
     * Get ces_mii_mac from soc properties
     */
    sal_memset(config->mac, 0, sizeof(sal_mac_addr_t));

    mac_str = soc_property_get_str(unit, spn_CES_MII_MAC);
    if (mac_str != NULL) {
	if (bcm_ces_parse_macaddr(mac_str, config->mac) < 0 ) {
	    return BCM_E_INTERNAL;                 
	}
    } else {
	config->mac[0] = 0x00;
	config->mac[1] = 0xF1;
	config->mac[2] = 0xF2;
	config->mac[3] = 0xF3;
	config->mac[4] = 0xF4;
	config->mac[5] = 0xF5;
    }


    /*
     * Get the IPv4 address
     */
    ip_str = soc_property_get_str(unit, spn_CES_IPV4_ADDRESS);

    if (ip_str != NULL) {
	bcm_ces_parse_ipaddr(ip_str, &config->ipv4_addr);
    } else {
	config->ipv4_addr = 0x0A0A0A0A;
    }

    
    ip_str = soc_property_get_str(unit, spn_CES_IPV6_ADDRESS);
    if (ip_str != NULL) {
	
#if 0
	if (bcm_ces_parse_ipv6addr(ip_str, config->ipv6_addr) < 0 ) {
	    return BCM_E_INTERNAL;                 
	}
#else
	sal_memset(config->ipv6_addr, 0x55, sizeof(ip6_addr_t));
#endif
    } else {
	sal_memset(config->ipv6_addr, 0xAA, sizeof(ip6_addr_t));
    }

    /*
     * MAC
     */
    n_ret = ag_nd_device_read(config->ndHandle, AG_ND_OPCODE_CONFIG_MAC, &config->ndMac);
    if (!AG_SUCCEEDED(n_ret))
    {
	return BCM_E_INTERNAL;
    }

    config->ndMac.n_frame_length = 1500; 
    config->ndMac.x_command_config.b_no_length_check     = AG_TRUE;
    config->ndMac.x_command_config.b_enable_mac_receive  = AG_TRUE;
    config->ndMac.x_command_config.b_enable_mac_transmit = AG_TRUE;
    config->ndMac.x_command_config.b_enable_promiscuous_mode = AG_TRUE;
    config->ndMac.x_command_config.b_fwd_pause_frames        = AG_TRUE;
    config->ndMac.x_command_config.b_enable_frame_padding    = AG_TRUE;

    sal_memcpy(config->ndMac.a_mac, config->mac, sizeof(sal_mac_addr_t));

    config->ndMac.n_rx_section_empty = BCM_CES_RX_SECTION_EMPTY;
    config->ndMac.n_rx_section_full  = BCM_CES_RX_SECTION_FULL;
    config->ndMac.n_tx_section_empty = BCM_CES_TX_SECTION_EMPTY;
    config->ndMac.n_tx_section_full  = BCM_CES_TX_SECTION_FULL;
    config->ndMac.n_rx_almost_empty  = BCM_CES_RX_ALMOST_EMPTY;
    config->ndMac.n_rx_almost_full   = BCM_CES_RX_ALMOST_FULL;
    config->ndMac.n_tx_almost_empty  = BCM_CES_TX_ALMOST_EMPTY;
    config->ndMac.n_tx_almost_full   = BCM_CES_TX_ALMOST_FULL;


    /*
     * global
     */
    n_ret = ag_nd_device_read(config->ndHandle, AG_ND_OPCODE_CONFIG_GLOBAL, &config->ndGlobal);
    if (!AG_SUCCEEDED(n_ret))
    {
	return BCM_E_INTERNAL;
    }

    config->ndGlobal.n_filler_byte = BCM_CES_FILLER_BYTE;
    config->ndGlobal.n_idle_byte   = BCM_CES_IDLE_BYTE;
    
    config->ndGlobal.a_lops_threshold_table[0] = BCM_CES_LOPS_THRESHOLD_TABLE;
    config->ndGlobal.a_aops_threshold_table[0] = BCM_CES_AOPS_THRESHOLD_TABLE; 
    config->ndGlobal.n_cw_mask = BCM_CES_CW_L | BCM_CES_CW_R | BCM_CES_CW_M;

    config->ndGlobal.n_cas_e1_idle_pattern = BCM_CES_CAS_T1_IDLE_PATTERN;
    config->ndGlobal.n_cas_t1_idle_pattern = BCM_CES_CAS_T1_IDLE_PATTERN;
    config->ndGlobal.n_cas_no_change_delay = BCM_CES_CAS_NO_CHANGE_DELAY;
    config->ndGlobal.n_cas_change_delay    = BCM_CES_CAS_CHANGE_DELAY;
    config->ndGlobal.n_cas_sqn_window      = BCM_CES_CAS_SQN_WINDOW;
    config->ndGlobal.n_rai_detect          = bcmCesRaiDetectM | bcmCesRaiDetectR;

    /*
     * policy
     */
    ag_nd_memset(&config->ndPolicy, 0, sizeof(config->ndPolicy));
    config->ndPolicy.x_policy_matrix.n_status_polarity = 0xffffffff;


    /*
     * ucode
     */
    sal_memcpy(config->ndUcode.a_dest_ipv6, config->ipv6_addr, sizeof(ip6_addr_t));
    sal_memcpy(config->ndUcode.a_dest_mac, config->mac, sizeof(sal_mac_addr_t));
    config->ndUcode.b_ecid_direct = AG_FALSE;
    config->ndUcode.b_mpls_direct = AG_FALSE;
    config->ndUcode.b_udp_direct  = AG_FALSE;
    config->ndUcode.n_dest_ipv4   = config->ipv4_addr;
    config->ndUcode.n_vlan_mask   = 0x0FFF;

    /*
     * Mark as configured
     */
    BCM_CES_SET_CONFIGURED(config);
    return BCM_E_NONE;
}


/**
 * Function:
 *      bcm_esw_ces_services_init
 * Purpose:
 *      Initialize CES services subsystem.
 * Parameters:
 *      unit - (IN) Unit number.
 * Returns:
 *      BCM_E_NONE Operation completed successfully
 *      BCM_E_INTERNAL An internal error occured
 * Notes:
 */
int 
bcm_esw_ces_services_init(int unit) {
    AgResult    n_ret;
    bcm_ces_service_global_config_t *config = SOC_CONTROL(unit)->ces_ctrl;
    int i;
    int ret;
    AgNdMsgConfigRpcPolicy policy;

    if ((ret = bcm_esw_ces_enable_mii_port(unit, config->ces_mii_port)) != BCM_E_NONE) {
	SOC_DEBUG_PRINT((DK_VERBOSE, "Failed to set GE0 port speed\n"));
	return BCM_E_INTERNAL;
    }

    /*
     * init 
     */
    n_ret = ag_nd_device_write(config->ndHandle, AG_ND_OPCODE_CONFIG_MAC, &config->ndMac);
    if (!AG_SUCCEEDED(n_ret))
    {
	return BCM_E_INTERNAL;
    }

    n_ret = ag_nd_device_write(config->ndHandle, AG_ND_OPCODE_CONFIG_GLOBAL, &config->ndGlobal);
    if (!AG_SUCCEEDED(n_ret))
    {
	return BCM_E_INTERNAL;
    }

    n_ret = ag_nd_device_write(config->ndHandle, AG_ND_OPCODE_CONFIG_RPC_POLICY, &config->ndPolicy);
    if (!AG_SUCCEEDED(n_ret))
    {
	return BCM_E_INTERNAL;
    }

    n_ret = ag_nd_device_write(config->ndHandle, AG_ND_OPCODE_CONFIG_RPC_UCODE, &config->ndUcode);
    if (!AG_SUCCEEDED(n_ret))
    {
	return BCM_E_INTERNAL;
    }

    
    bcm_esw_ces_service_destroy_all(unit);

    /*
     * Initialize service records.
     */
    for (i = 0;i < BCM_CES_CIRCUIT_IDX_MAX;i++)
    {
	if ((ret = bcm_ces_service_init_record(unit, i)) != BCM_E_NONE)
	{
	    return ret;
	}
    }

    /*
     * Set RPC default policy
     */
    sal_memset(&policy, 0, sizeof(AgNdMsgConfigRpcPolicy));

    policy.x_policy_matrix.n_status_polarity     = 0xFFFFFFFF;
    policy.x_policy_matrix.n_drop_unconditional  = 0xF1F25EC0;
    policy.x_policy_matrix.n_drop_if_not_forward = 0x00000000;
    policy.x_policy_matrix.n_forward_to_ptp      = 0x00000000;
    policy.x_policy_matrix.n_forward_to_host_high = 0x00000000;
    policy.x_policy_matrix.n_forward_to_host_low  = 0x00000000;
    policy.x_policy_matrix.n_channel_counter_1 = 0x11004000;
    policy.x_policy_matrix.n_channel_counter_2 = 0x02000000;
    policy.x_policy_matrix.n_channel_counter_3 = 0x04000000;
    policy.x_policy_matrix.n_channel_counter_4 = 0x00008000;
    policy.x_policy_matrix.n_global_counter_1 = 0xF0000000;
    policy.x_policy_matrix.n_global_counter_2 = 0x0F000000;
    policy.x_policy_matrix.n_global_counter_3 = 0x00F00000;
    policy.x_policy_matrix.n_global_counter_4 = 0x000F0000;
    policy.x_policy_matrix.n_global_counter_5 = 0x0000F000;
    policy.x_policy_matrix.n_global_counter_6 = 0x00000F00;
    policy.x_policy_matrix.n_global_counter_7 = 0x000000F0;
    policy.x_policy_matrix.n_global_counter_8 = 0x0000000F;

    if (bcm_esw_ces_rpc_pm_set(unit, &policy)!= BCM_E_NONE)
	return BCM_E_INTERNAL;

    /*
     * Initialize the rclock instances
     */
    memset(config->rclock_record, 0 , sizeof(bcm_ces_rclock_record_t) * BCM_CES_RCLOCK_MAX);

    return BCM_E_NONE; 
}


/**
 * Function:
 *      bcm_ces_services_clear
 * Purpose:
 *      Initialize CES services subsystem without affecting current
 *      hardware state.
 * Parameters:
 *      unit - (IN) Unit number.
 * Returns:
 *      BCM_E_xxx
 * Notes:
 */
int 
bcm_esw_ces_services_clear(
    int unit)
{
    return BCM_E_UNAVAIL; 
}

/**
 * Function:
 *      bcm_esw_ces_service_config_channelizer
 * Purpose:
 *      Utility method to configure a channelizer
 * Parameters:
 *      unit - (IN) Unit number.
 *      config - (IN) <UNDEF>
 *      ces_service - (OUT) CES Channel
 *      path - (IN)
 * Returns:
 *      BCM_E_xxx
 * Notes:
 */
int 
bcm_esw_ces_service_config_channelizer(
    int unit, 
    bcm_ces_service_record_t *record,
    bcm_ces_channel_map_t *map,
    int path)
{
    AgResult n_ret;
    AgNdMsgConfigChannelizer ndChannelizer;
    bcm_ces_service_global_config_t *ctrl = SOC_CONTROL(unit)->ces_ctrl;
    int i;

    /*
     * chi
     */
    sal_memset(&ndChannelizer, 0, sizeof(AgNdMsgConfigChannelizer));
    ndChannelizer.e_path        = path;
    ndChannelizer.n_channel_id  = (AgNdChannel)record->ces_service;
    ndChannelizer.x_map.n_first = map->first;
    ndChannelizer.x_map.n_size  = map->size;
    for (i = map->first;i < (map->first + map->size);i++)
    {
	ndChannelizer.x_map.a_ts[i].n_circuit_id = (AgNdCircuit) bcm_ces_port_to_circuit_id(unit, map->circuit_id[i]);
	ndChannelizer.x_map.a_ts[i].n_slot_idx = map->slot[i];
    }

    n_ret = ag_nd_device_write(ctrl->ndHandle, AG_ND_OPCODE_CONFIG_CHANNELIZER, &ndChannelizer);
    if (!AG_SUCCEEDED(n_ret))
    {
	return BCM_E_INTERNAL;
    }

    return BCM_E_NONE;
}

/**
 * Function:
 *      bcm_ces_copy_header
 * Purpose:
 *      Copy header from BRCM to BATM format
 * Parameters:
 *      unit - (IN) Unit number.
 * Returns:
 * Notes:
 */
void bcm_ces_copy_header(bcm_ces_packet_header_t *src, AgNdPacketHeader *dest) {
    int i;

    sal_memcpy(dest->x_eth.a_source, src->eth.source, sizeof(sal_mac_addr_t));
    sal_memcpy(dest->x_eth.a_destination, src->eth.destination, sizeof(sal_mac_addr_t));
    dest->e_encapsulation = src->encapsulation;

    dest->x_ip4.n_tos = src->ipv4.tos;
    dest->x_ip4.n_ttl = src->ipv4.ttl;
    dest->x_ip4.n_source = src->ipv4.source;
    dest->x_ip4.n_destination = src->ipv4.destination;

    dest->x_vc_label.n_label = src->vc_label;
    dest->n_ip_version = src->ip_version;

    dest->n_vlan_count = src->vlan_count;
    for (i = 0;i < src->vlan_count;i++) {
	dest->a_vlan[i].n_priority = src->vlan[i].priority;
	dest->a_vlan[i].n_vid = src->vlan[i].vid;
    }

    dest->n_mpls_count = src->mpls_count;
    for (i = 0;i < src->mpls_count;i++) {
	 dest->a_mpls[i].n_label = src->mpls[i].label;
	 dest->a_mpls[i].n_expiremental = src->mpls[i].experimental;
	 dest->a_mpls[i].n_ttl = src->mpls[i].ttl;
    }

    dest->x_udp.n_destination = src->udp.destination;
    dest->x_udp.n_source = src->udp.source;

    dest->b_rtp_exists = src->rtp_exists;
    dest->x_rtp.n_pt = src->rtp_pt;
    dest->x_rtp.n_ssrc = src->rtp_ssrc;

    dest->b_udp_chksum = src->udp_chksum;

    dest->n_l2tpv3_count = 0; 
    dest->x_l2tpv3.b_udp_mode = src->l2tpv3.udp_mode;
    dest->x_l2tpv3.n_header = src->l2tpv3.header;
    dest->x_l2tpv3.n_session_local_id = src->l2tpv3.session_local_id;
    dest->x_l2tpv3.n_session_peer_id = src->l2tpv3.session_peer_id;
    dest->x_l2tpv3.n_local_cookie1   = src->l2tpv3.local_cookie1;
    dest->x_l2tpv3.n_local_cookie2   = src->l2tpv3.local_cookie2;
    dest->x_l2tpv3.n_peer_cookie1    = src->l2tpv3.peer_cookie1;
    dest->x_l2tpv3.n_peer_cookie2    = src->l2tpv3.peer_cookie2;

    sal_memcpy(dest->x_ip6.a_source, src->ipv6.source, 16);
    sal_memcpy(dest->x_ip6.a_destination, src->ipv6.destination, 16);
    dest->x_ip6.n_traffic_class = src->ipv6.traffic_class;
    dest->x_ip6.n_flow_label = src->ipv6.flow_label;
    dest->x_ip6.n_hop_limit  = src->ipv6.hop_limit;
}

/**
 * Function:
 *      bcm_ces_init_set_mac_address
 * Purpose:
 *      Replace the MAC address in the uCode. This is the system MAC (source address).
 * Parameters:
 *      unit - (IN) Unit number.
 *      flags - (IN) <UNDEF>
 *      config - (IN) <UNDEF>
 *      ces_service - (OUT) CES Channel
 * Returns:
 *      BCM_E_NONE Operation completed successfully
 *      BCM_E_BADID Invalid service ID was used
 *      BCM_E_PARAM The config parameter was NULL
 *      BCM_E_INTERNAL An internal error occurred
 * Notes:
 */
AgResult bcm_ces_init_set_mac_address(bcm_ces_service_global_config_t *ctrl, AG_U8 *p_mac_address) {
    AgResult res;
    AgNdMsgConfigRpcUcode x_rpc_msg;

    sal_memset(&x_rpc_msg,0x00,sizeof(AgNdMsgConfigRpcUcode));
    res = ag_nd_device_read(ctrl->ndHandle, AG_ND_OPCODE_CONFIG_RPC_UCODE, &x_rpc_msg);

    /*set mac address*/
    sal_memcpy(x_rpc_msg.a_dest_mac,p_mac_address,6);

    res = ag_nd_device_write(ctrl->ndHandle, AG_ND_OPCODE_CONFIG_RPC_UCODE, &x_rpc_msg);
    if (AG_FAILED(res)) {
       /* BOARD_ERR_LOG0("CAgFPGAFWNemo::set_mac_address  Error configuring RPC machine");* */
        return res;
    }

    return AG_S_OK;
}

/**
 * Function:
 *      bcm_ces_init_set_ip_address_in_RPC_machine
 * Purpose:
 *      Replace IP address in uCode. This is the system IP (source address).
 * Parameters:
 *      unit - (IN) Unit number.
 *      flags - (IN) <UNDEF>
 *      config - (IN) <UNDEF>
 *      ces_service - (OUT) CES Channel
 * Returns:
 *      BCM_E_NONE Operation completed successfully
 *      BCM_E_BADID Invalid service ID was used
 *      BCM_E_PARAM The config parameter was NULL
 *      BCM_E_INTERNAL An internal error occurred
 * Notes:
 */
AgResult bcm_ces_init_set_ip_address_in_RPC_machine(bcm_ces_service_global_config_t *ctrl, AG_U32 IP_address) {
    AgResult res = AG_S_OK;
    AgNdMsgConfigRpcUcode x_rpc_msg;

    sal_memset(&x_rpc_msg,0x00,sizeof(AgNdMsgConfigRpcUcode));
    res = ag_nd_device_read(ctrl->ndHandle, AG_ND_OPCODE_CONFIG_RPC_UCODE, &x_rpc_msg);
    /*Set IP address*/
    x_rpc_msg.n_dest_ipv4 = IP_address;

    res = ag_nd_device_write(ctrl->ndHandle, AG_ND_OPCODE_CONFIG_RPC_UCODE, &x_rpc_msg);
    if (AG_FAILED(res)) {
        /*BOARD_ERR_LOG0("Error configuring RPC machine");*/
        return res;
    }

    return res;
}


/**
 * Function:
 *      bcm_esw_ces_service_create
 * Purpose:
 *      Allocate and configure a CES service.
 * Parameters:
 *      unit - (IN) Unit number.
 *      flags - (IN) <UNDEF>
 *      config - (IN) <UNDEF>
 *      ces_service - (OUT) CES Channel
 * Returns:
 *      BCM_E_NONE Operation completed successfully
 *      BCM_E_BADID Invalid service ID was used
 *      BCM_E_PARAM The config parameter was NULL
 *      BCM_E_INTERNAL An internal error occurred
 * Notes:
 */
int 
bcm_esw_ces_service_create(
    int unit, 
    int flags, 
    bcm_ces_service_config_t *config, 
    bcm_ces_service_t *ces_service)
{
    AgResult n_ret;
    bcm_ces_service_record_t *record;
    int i;
    bcm_ces_channel_map_t map;
    AgNdMsgConfigCircuitEnable     ndCircuitEnable;
    AgNdMsgConfigChannelEgress     ndChannelEgress;
    AgNdMsgConfigChannelIngress    ndChannelIngress;
    AgNdMsgConfigNemoCircuit       ndCircuit;
    AgFramerConfig                 ndFramer;
    AgNdMsgConfigCasChannelIngress ndCasIngress;
    bcm_ces_service_global_config_t *ctrl = SOC_CONTROL(unit)->ces_ctrl;
    int egressChannelMapChange   = TRUE;
    int ingressChannelMapChange  = TRUE;
    int channelEgressChange      = TRUE;
    int channelIngressChange     = TRUE;
    int channelizerEgressChange  = TRUE;
    int channelizerIngressChange = TRUE;
    int nemoCircuitChange        = TRUE;
    int headerChange             = TRUE;
    int strictDataChange         = TRUE;
    int destMacChange            = TRUE;
    int encapsulationChange      = TRUE;
    int enabled                  = FALSE;
#if 0
#ifdef BCM_CES_CRM_SUPPORT 
    bcm_ces_crm_port_config_msg_t port_msg;
#endif
#endif
    int16 modPorts[BCM_CES_SLOT_MAX];
    int j;

    memset(modPorts, -1, sizeof(modPorts));

    /*
     * If this is a new service then get a service (channel) 
     * and allocate a service record.
     */
    if (!(flags & BCM_CES_WITH_ID) && !(flags & BCM_CES_TDM_UPDATE_WITH_ID)) {
	n_ret = bcm_ces_service_get_service(unit, ces_service, &record, FALSE);
	if (n_ret != BCM_E_NONE)
	    return n_ret;
    } else if ((flags & BCM_CES_WITH_ID) && !(flags & BCM_CES_TDM_UPDATE_WITH_ID) &&
	       (n_ret = bcm_ces_service_get_service(unit, ces_service, &record, TRUE) == BCM_E_NONE)) {
	/*
	 * Create service with specified ID
	 *
	 * Supress the flag. It will now be used just to indicate a modify
	 */
	flags &= ~BCM_CES_WITH_ID;
    } else {
	/*
	 * Modify existing service record
	 */

	n_ret = bcm_ces_service_find(unit, *ces_service, &record);
	if (n_ret != BCM_E_NONE)
	    return n_ret;

	if (BCM_CES_IS_FREE(&record->config))
	    return BCM_E_UNAVAIL;

	/*
	 * If enabled then disable.
	 */
	if (BCM_CES_IS_ENABLED(&record->config)) {
	    enabled = TRUE;
	    bcm_esw_ces_service_enable_set(unit, *ces_service, 0);
	}

	if (flags & BCM_CES_WITH_ID) {
	    /*
	     * Identify configuration changes
	     */
	    if (sal_memcmp(&config->ingress_channel_map, 
			   &record->config.ingress_channel_map, 
			   sizeof(bcm_ces_channel_map_t)) == 0)
		ingressChannelMapChange  = FALSE;

	    if (sal_memcmp(&config->egress_channel_map, 
			   &record->config.egress_channel_map, 
			   sizeof(bcm_ces_channel_map_t)) == 0)
		egressChannelMapChange  = FALSE;

	    if (sal_memcmp(&config->header, 
			   &record->config.header, 
			   sizeof(bcm_ces_packet_header_t)) == 0)
		headerChange  = FALSE;

	    if (sal_memcmp(&config->strict_data, 
			   &record->config.strict_data, 
			   sizeof(bcm_ces_packet_header_t)) == 0)
		strictDataChange  = FALSE;

	    if (sal_memcmp(&config->egress_channel_config, 
			   &record->config.egress_channel_config, 
			   sizeof(bcm_ces_egress_channel_config_t)) == 0)
		channelEgressChange = FALSE;

	    if (sal_memcmp(&config->ingress_channel_config, 
			   &record->config.ingress_channel_config, 
			   sizeof(bcm_ces_ingress_channel_config_t)) == 0)
		channelIngressChange = FALSE;


	    if (sal_memcmp(&config->dest_mac, 
			   &record->config.dest_mac, 
			   sizeof(bcm_mac_t)) == 0)
		destMacChange = FALSE;

	    if (sal_memcmp(&config->encapsulation, 
			   &record->config.encapsulation, 
			   sizeof(bcm_ces_encapsulation_t)) == 0)
		encapsulationChange = FALSE;


	    channelizerIngressChange = FALSE;
	    channelizerEgressChange  = FALSE;

	    nemoCircuitChange        = FALSE; 

	    /*
	     * If there are no changes then we are done
	     */
	    if (egressChannelMapChange  == FALSE &&
		ingressChannelMapChange  == FALSE &&
		channelEgressChange      == FALSE &&
		channelIngressChange     == FALSE &&
		channelizerEgressChange  == FALSE &&
		channelizerIngressChange == FALSE &&
		nemoCircuitChange        == FALSE &&
		headerChange             == FALSE &&
		strictDataChange         == FALSE &&
		destMacChange            == FALSE &&
		encapsulationChange      == FALSE)
		return BCM_E_NONE;

	    /*
	     * If the channel mapping has changed then disable and remove existing
	     * channel mapping.  
	     */
	    if (ingressChannelMapChange || egressChannelMapChange) {

		if (ingressChannelMapChange) {

		    /*
		     * Remove ingress channel map
		     */
		    sal_memset(&map, 0, sizeof(bcm_ces_channel_map_t));
		    n_ret = bcm_esw_ces_service_config_channelizer(unit, record, &map, AG_ND_PATH_INGRESS);
		}

		if (egressChannelMapChange) {

		    /*
		     * Remove channel map
		     */
		    sal_memset(&map, 0, sizeof(bcm_ces_channel_map_t));
		    n_ret = bcm_esw_ces_service_config_channelizer(unit, record, &map, AG_ND_PATH_EGRESS);
		}
	    }
	} else if (flags & BCM_CES_TDM_UPDATE_WITH_ID) {
	    /*
	     * TDM only update. Config does not have to be provided.
	     */
	    if (config == NULL)
		config = &record->config;
	    egressChannelMapChange   = FALSE;
	    ingressChannelMapChange  = FALSE;
	    channelEgressChange      = FALSE;
	    channelIngressChange     = FALSE;
	    channelizerEgressChange  = FALSE;
	    channelizerIngressChange = FALSE;
	    headerChange             = FALSE;
	    strictDataChange         = FALSE;
	    destMacChange            = FALSE;
	    encapsulationChange      = FALSE;
	    nemoCircuitChange        = TRUE;
	}
    }

    /*
     * Config must be set 
     */
    if (config == NULL)
	return(BCM_E_PARAM);

    /*
     * Make sure that no random flags are set
     */
    BCM_CES_CLR_ALL(config);

    /*
     * Save the requested config
     */
    if (config != &record->config) {
	sal_memcpy(&record->config, config, sizeof(bcm_ces_service_config_t)); 
	BCM_CES_SET_MODIFIED(&record->config);
    }
    
    if (!flags)
	record->ces_service = *ces_service;

    /*
     * For each circuit in the ingress channel map:
     * - Disable
     * - Config
     */
    for (i = config->ingress_channel_map.first;i < (config->ingress_channel_map.first + config->ingress_channel_map.size);i++)
    {
	/*
	 * Has the port state already been modified?
	 */
	j = 0;
	while(modPorts[j] != -1 && j < BCM_CES_SLOT_MAX) {
	    if (modPorts[j] == (int16)record->config.ingress_channel_map.circuit_id[i])
		break;
	    j++;
	}

	if (modPorts[j] != (int16)record->config.ingress_channel_map.circuit_id[i]) {
	    /*
	     * Make sure that the circuit is disabled
	     */
	    ndCircuitEnable.n_circuit_id = (AgNdCircuit) bcm_ces_port_to_circuit_id(unit, config->ingress_channel_map.circuit_id[i]);
	    ndCircuitEnable.b_enable = AG_FALSE;
	    n_ret = ag_nd_device_write(ctrl->ndHandle, AG_ND_OPCODE_CONFIG_CIRCUIT_ENABLE, &ndCircuitEnable);
	    if (!AG_SUCCEEDED(n_ret))
	    {
		SOC_DEBUG_PRINT((DK_VERBOSE, "AG_ND_OPCODE_CONFIG_CIRCUIT_ENABLE failed\n"));
		if (!(flags & BCM_CES_WITH_ID))
		    bcm_ces_service_free_record(unit, *ces_service);
		return BCM_E_INTERNAL;
	    }

	    /*
	     * Disable framer. Note that the enable also can set parameters so recover
	     * the existing config before disabling.
	     */
	    sal_memset(&ndFramer, 0, sizeof(AgFramerConfig));
	    ndFramer.n_circuit_id = (AgNdCircuit) bcm_ces_port_to_circuit_id(unit, config->ingress_channel_map.circuit_id[i]);
	    n_ret = ag_nd_device_read(ctrl->ndHandle, AG_ND_OPCODE_FRAMER_PORT_CONFIG, &ndFramer);
	    if (!AG_SUCCEEDED(n_ret)) {
		SOC_DEBUG_PRINT((DK_VERBOSE, "AG_ND_OPCODE_FRAMER_PORT_CONFIG failed\n"));
		if (!(flags & BCM_CES_WITH_ID))
		    bcm_ces_service_free_record(unit, *ces_service);
		return BCM_E_INTERNAL;
	    }

	    ndFramer.b_enable = FALSE;

	    n_ret = ag_nd_device_write(ctrl->ndHandle, AG_ND_OPCODE_FRAMER_PORT_CONFIG, &ndFramer);
	    if (!AG_SUCCEEDED(n_ret)) {
		SOC_DEBUG_PRINT((DK_VERBOSE, "AG_ND_OPCODE_FRAMER_PORT_CONFIG failed\n"));
		if (!(flags & BCM_CES_WITH_ID))
		    bcm_ces_service_free_record(unit, *ces_service);
		return BCM_E_INTERNAL;
	    }


	    /*
	     * Find associated TDM port
	     */
	    if ((record->tdmRecord = bcm_esw_port_tdm_find_port(unit,(AgNdCircuit) config->ingress_channel_map.circuit_id[i])) == NULL)
	    {
		SOC_DEBUG_PRINT((DK_VERBOSE, "bcm_esw_port_tdm_find_port() failed\n"));
		if (!(flags & BCM_CES_WITH_ID))
		    bcm_ces_service_free_record(unit, *ces_service);
		return BCM_E_INTERNAL;
	    }

	    if (ingressChannelMapChange || egressChannelMapChange) {
		/*
		 * Add to TDM port
		 */
		n_ret = bcm_esw_port_tdm_add_service(unit,
						     record->tdmRecord, 
						     record,
						     config->ingress_channel_map.size, 
						     config->ingress_channel_map.slot);
		if (n_ret != BCM_E_NONE)
		{
		    SOC_DEBUG_PRINT((DK_VERBOSE, "bcm_esw_port_tdm_add_service() failed\n"));
		    bcm_esw_ces_service_free_tdm(unit, *ces_service);
		    if (!(flags & BCM_CES_WITH_ID))
			bcm_ces_service_free_record(unit, *ces_service);
		    return BCM_E_INTERNAL;
		}
	    }

	    if (nemoCircuitChange) {

		/*
		 * Recover circuit configuration
		 */
		sal_memset(&ndCircuit, 0, sizeof(AgNdCircuit));
		ndCircuit.n_circuit_id     = (AgNdCircuit) bcm_ces_port_to_circuit_id(unit, record->tdmRecord->port);

		if (flags & BCM_CES_TDM_UPDATE_WITH_ID) {
		    n_ret = ag_nd_device_read(ctrl->ndHandle, AG_ND_OPCODE_CONFIG_NEMO_CIRCUIT, &ndCircuit);
		    if (!AG_SUCCEEDED(n_ret)) {
			SOC_DEBUG_PRINT((DK_VERBOSE, "AG_ND_OPCODE_CONFIG_NEMO_CIRCUIT failed\n"));
			bcm_esw_ces_service_free_tdm(unit, *ces_service);
			if (!(flags & BCM_CES_WITH_ID))
			    bcm_ces_service_free_record(unit, *ces_service);
			return BCM_E_INTERNAL;
		    }
		}

		ndCircuit.n_circuit_id     = (AgNdCircuit) bcm_ces_port_to_circuit_id(unit, record->tdmRecord->port);
		ndCircuit.e_tdm_protocol   = record->tdmRecord->config.e_protocol;
		ndCircuit.b_structured     = record->tdmRecord->config.b_structured;
		ndCircuit.b_octet_aligned  = record->tdmRecord->config.b_octet_aligned;
		ndCircuit.b_signaling_enable = record->tdmRecord->config.b_signaling_enable;
		ndCircuit.b_T1_D4_framing  = record->tdmRecord->config.b_T1_D4_framing;
		ndCircuit.e_clk_rx_select  = record->tdmRecord->config.e_clk_rx_select;
		ndCircuit.e_clk_tx_select  = record->tdmRecord->config.e_clk_tx_select;

		n_ret = ag_nd_device_write(ctrl->ndHandle, AG_ND_OPCODE_CONFIG_NEMO_CIRCUIT, &ndCircuit);
		if (!AG_SUCCEEDED(n_ret))
		{
		    SOC_DEBUG_PRINT((DK_VERBOSE, "AG_ND_OPCODE_CONFIG_NEMO_CIRCUIT failed\n"));
		    bcm_esw_ces_service_free_tdm(unit, *ces_service);
		    if (!(flags & BCM_CES_WITH_ID))
			bcm_ces_service_free_record(unit, *ces_service);
		    return BCM_E_INTERNAL;
		}

		/*
		 * Framer configuration
		 */
		sal_memset(&ndFramer, 0, sizeof(AgFramerConfig));
		ndFramer.n_circuit_id     = (AgNdCircuit) bcm_ces_port_to_circuit_id(unit, record->tdmRecord->port);
		ndFramer.b_enable = FALSE;
		ndFramer.b_t1 = (record->tdmRecord->config.e_protocol == bcmCesTdmProtocolT1 ? 1:0);
		ndFramer.b_esf = !(record->tdmRecord->config.b_T1_D4_framing);
		ndFramer.b_transmit_crc = record->tdmRecord->config.b_rxcrc;
		ndFramer.b_recive_crc   = record->tdmRecord->config.b_txcrc;
		ndFramer.n_singling_format = record->tdmRecord->config.n_signaling_format; 
		ndFramer.b_master = record->tdmRecord->config.b_master;

		n_ret = ag_nd_device_write(ctrl->ndHandle, AG_ND_OPCODE_FRAMER_PORT_CONFIG, &ndFramer);
		if (!AG_SUCCEEDED(n_ret))
		{
		    SOC_DEBUG_PRINT((DK_VERBOSE, "AG_ND_OPCODE_FRAMER_PORT_CONFIG failed\n"));
		    bcm_esw_ces_service_free_tdm(unit, *ces_service);
		    if (!(flags & BCM_CES_WITH_ID))
			bcm_ces_service_free_record(unit, *ces_service);
		    return BCM_E_INTERNAL;
		}

#if 0
		/*
		 * Send to CRM
		 */
#ifdef BCM_CES_CRM_SUPPORT 
		sal_memset(&port_msg, 0, sizeof(bcm_ces_crm_port_config_msg_t));
		port_msg.flags = 0x00;
		port_msg.port  = (AgNdCircuit)bcm_ces_port_to_circuit_id(unit, record->tdmRecord->port);
		port_msg.e_clk_rx_select = record->tdmRecord->config.e_clk_rx_select;
		port_msg.e_clk_tx_select = record->tdmRecord->config.e_clk_tx_select;

		n_ret =  bcm_esw_ces_crm_dma_msg_send(unit, MOS_MSG_SUBCLASS_CES_CRM_PORT_CONFIG, (uint8*)&port_msg, sizeof(bcm_ces_crm_port_config_msg_t));
		if (n_ret != BCM_E_NONE)
		{
		    SOC_DEBUG_PRINT((DK_VERBOSE, "MOS_MSG_SUBCLASS_CES_CRM_PORT_CONFIG failed\n"));
		    bcm_esw_ces_service_free_tdm(unit, *ces_service);
		    if (!(flags & BCM_CES_WITH_ID))
			bcm_ces_service_free_record(unit, *ces_service);
		    return BCM_E_INTERNAL;
		}
#endif
#endif
	    }
	    modPorts[j] = (int16)record->config.ingress_channel_map.circuit_id[i];
	}
    }


    /*
     * Channel Ingress
     */
    if (channelIngressChange || headerChange) {
        sal_memset(&ndChannelIngress, 0, sizeof(AgNdMsgConfigChannelIngress)); 
	ndChannelIngress.n_channel_id   = (AgNdChannel)*ces_service;

	ndChannelIngress.b_redundancy_enabled = AG_FALSE;
	ndChannelIngress.b_redundancy_config = 0x00;

	ndChannelIngress.n_channel_id   = (AgNdChannel)*ces_service;

	if (config->ingress_channel_map.slot[0] != 0) 
	    config->ingress_channel_config.payload_size = config->ingress_channel_map.size * 8;
	else if (ctrl->protocol == bcmCesTdmProtocolE1)
	    config->ingress_channel_config.payload_size = BCM_CES_E1_PAYLOAD_SIZE;
	else
	    config->ingress_channel_config.payload_size = BCM_CES_T1_PAYLOAD_SIZE;

	ndChannelIngress.n_payload_size = config->ingress_channel_config.payload_size;
	ndChannelIngress.b_dba          = config->ingress_channel_config.dba;
	ndChannelIngress.n_pbf_size     = config->ingress_channel_config.pbf_size;


	ndChannelIngress.x_header.e_encapsulation = config->header.encapsulation;

	/*
	 * common for all encapsulations
	 */
	sal_memcpy(ndChannelIngress.x_header.x_eth.a_source, config->header.eth.source, 6);
	sal_memcpy(ndChannelIngress.x_header.x_eth.a_destination, config->header.eth.destination, 6);

	ndChannelIngress.x_header.b_udp_chksum = config->header.udp_chksum;

	/*
	 * Make sure that required information is available for 
	 * the selected encapsulation type.
	 */
	switch(config->header.encapsulation)
	{
	case bcmCesEncapsulationEth:
	    ndChannelIngress.x_header.n_mpls_count = 0;
	    ndChannelIngress.x_header.n_l2tpv3_count = 0;
	    break;

	case bcmCesEncapsulationIp:
	    ndChannelIngress.x_header.n_mpls_count = 0;
	    ndChannelIngress.x_header.n_l2tpv3_count = 0;
	    break;

	case bcmCesEncapsulationMpls:
	    ndChannelIngress.x_header.n_l2tpv3_count = 0;

	    if (config->header.mpls_count == 0 ||
		config->header.mpls_count > BCM_CES_PROTO_MPLS_LABEL_MAX) {
		
		return BCM_E_PARAM;
	    }

	    ndChannelIngress.x_header.n_mpls_count = config->header.mpls_count; 
	    for (i = 0;i < config->header.mpls_count;i++) {
		ndChannelIngress.x_header.a_mpls[i].n_label = config->header.mpls[i].label;
		ndChannelIngress.x_header.a_mpls[i].n_expiremental = config->header.mpls[i].experimental;
		ndChannelIngress.x_header.a_mpls[i].n_ttl = config->header.mpls[i].ttl;
	    }
	    break;

	case bcmCesEncapsulationL2tp:
	    ndChannelIngress.x_header.n_mpls_count = 0;
	    break;
	}

	/*
	 * If VLAN count greate than 0 then setup vlan
	 */
	if (config->header.vlan_count > 0) {
	    if (config->header.vlan_count > BCM_CES_PROTO_VLAN_TAG_MAX) {
		SOC_DEBUG_PRINT((DK_VERBOSE, "VLAN count out of range\n"));
		bcm_esw_ces_service_free_tdm(unit, *ces_service);
		if (!(flags & BCM_CES_WITH_ID))
		    bcm_ces_service_free_record(unit, *ces_service);
		return BCM_E_PARAM;
	    }

	    ndChannelIngress.x_header.n_vlan_count = config->header.vlan_count;

	    for (i = 0;i <  config->header.vlan_count;i++) {
		ndChannelIngress.x_header.a_vlan[i].n_priority = config->header.vlan[i].priority;
		ndChannelIngress.x_header.a_vlan[i].n_vid = config->header.vlan[i].vid;
	    }

	} else {
	    ndChannelIngress.x_header.n_vlan_count = 0;
	}

	/*
	 * VC Label
	 */
	ndChannelIngress.x_header.x_vc_label.n_label = config->header.vc_label;


	/*
	 * RTP 
	 */
	ndChannelIngress.x_header.b_rtp_exists = config->header.rtp_exists;
	ndChannelIngress.x_header.x_rtp.n_pt = config->header.rtp_pt;
	ndChannelIngress.x_header.x_rtp.n_ssrc = config->header.rtp_ssrc;
	if (config->egress_channel_config.rtp_exists != config->header.rtp_exists) {
	    config->egress_channel_config.rtp_exists = config->header.rtp_exists;
	    channelEgressChange = TRUE;
	}


	/*
	 * IP setup. 
	 */
	ndChannelIngress.x_header.n_ip_version = config->header.ip_version;

	if (config->header.encapsulation == bcmCesEncapsulationIp ||
	    config->header.encapsulation == bcmCesEncapsulationL2tp) {

	    switch (config->header.ip_version) {
	    case 4:
		ndChannelIngress.x_header.x_ip4.n_destination = config->header.ipv4.destination;
		ndChannelIngress.x_header.x_ip4.n_source      = config->header.ipv4.source;
		ndChannelIngress.x_header.x_ip4.n_tos         = config->header.ipv4.tos;
		ndChannelIngress.x_header.x_ip4.n_ttl         = config->header.ipv4.ttl;

		break;

	    case 6:
		ndChannelIngress.x_header.x_ip6.n_traffic_class = config->header.ipv6.traffic_class;
		ndChannelIngress.x_header.x_ip6.n_flow_label    = config->header.ipv6.flow_label;
		ndChannelIngress.x_header.x_ip6.n_hop_limit     = config->header.ipv6.hop_limit;

		sal_memcpy(ndChannelIngress.x_header.x_ip6.a_destination,
			   config->header.ipv6.destination,
			   sizeof(ip6_addr_t));
		sal_memcpy(ndChannelIngress.x_header.x_ip6.a_source,
			   config->header.ipv6.source,
			   sizeof(ip6_addr_t));
		break;

	    default:
		
		return BCM_E_PARAM;
		break;
	    }

	    if (config->header.encapsulation == bcmCesEncapsulationL2tp) {
		ndChannelIngress.x_header.x_l2tpv3.b_udp_mode = config->header.l2tpv3.udp_mode;
		ndChannelIngress.x_header.x_l2tpv3.n_header = config->header.l2tpv3.header;
		ndChannelIngress.x_header.x_l2tpv3.n_session_local_id = config->header.l2tpv3.session_local_id;
		ndChannelIngress.x_header.x_l2tpv3.n_session_peer_id = config->header.l2tpv3.session_peer_id;
		ndChannelIngress.x_header.x_l2tpv3.n_local_cookie1 = config->header.l2tpv3.local_cookie1;
		ndChannelIngress.x_header.x_l2tpv3.n_local_cookie2 = config->header.l2tpv3.local_cookie2;
		ndChannelIngress.x_header.x_l2tpv3.n_peer_cookie1 = config->header.l2tpv3.peer_cookie1;
		ndChannelIngress.x_header.x_l2tpv3.n_peer_cookie2 = config->header.l2tpv3.peer_cookie2;
	    } else {
		ndChannelIngress.x_header.x_udp.n_destination = config->header.udp.destination;
		ndChannelIngress.x_header.x_udp.n_source      = config->header.udp.source;
	    }
	} else {
	    ndChannelIngress.x_header.x_udp.n_destination = config->header.udp.destination;
	    ndChannelIngress.x_header.x_udp.n_source      = config->header.udp.source;
	}


	ndChannelIngress.n_pbf_addr = 0x00;

	n_ret = ag_nd_device_write(ctrl->ndHandle, AG_ND_OPCODE_CONFIG_CHANNEL_INGRESS, &ndChannelIngress);
	if (!AG_SUCCEEDED(n_ret))
	{
	    SOC_DEBUG_PRINT((DK_VERBOSE, "AG_ND_OPCODE_CONFIG_CHANNEL_INGRESS failed\n"));
	    bcm_esw_ces_service_free_tdm(unit, *ces_service);
	    if (!(flags & BCM_CES_WITH_ID))
		bcm_ces_service_free_record(unit, *ces_service);
	    return BCM_E_INTERNAL;
	}

	/*
	 * Setup CAS header
	 *
	 * Clone the service header and then adjust the labels to +1 from 
	 * the service
	 *
	 */
	ndCasIngress.n_channel_id = *ces_service;
	sal_memcpy(&ndCasIngress.x_header, &ndChannelIngress.x_header, sizeof(AgNdPacketHeader));

	switch(ndCasIngress.x_header.e_encapsulation)
	{
	case AG_ND_ENCAPSULATION_ETH:
	    ndCasIngress.x_header.x_vc_label.n_label = ndCasIngress.x_header.x_vc_label.n_label + 1;
	    break;

	case AG_ND_ENCAPSULATION_IP:
	    ndCasIngress.x_header.x_udp.n_destination = ndCasIngress.x_header.x_udp.n_destination + 1;
	    break;

	case AG_ND_ENCAPSULATION_MPLS:
	    ndCasIngress.x_header.a_mpls[0].n_label = ndCasIngress.x_header.a_mpls[0].n_label + 1;
	    break;

	case AG_ND_ENCAPSULATION_L2TP:
	    ndCasIngress.x_header.x_l2tpv3.n_session_peer_id = ndCasIngress.x_header.x_l2tpv3.n_session_peer_id + 1;
	    break;

	default:
	    /* Not handled */
	case AG_ND_ENCAPSULATION_PTP_IP:
	case AG_ND_ENCAPSULATION_PTP_EHT:
	case AG_ND_ENCAPSULATION_MAX:
	    break;
	}

	n_ret = ag_nd_device_write(ctrl->ndHandle, AG_ND_OPCODE_CONFIG_CAS_CHANNEL_INGRESS, &ndCasIngress);
	if (!AG_SUCCEEDED(n_ret))
	{
	    SOC_DEBUG_PRINT((DK_VERBOSE, "AG_ND_OPCODE_CONFIG_CAS_CHANNEL_INGRESS failed\n"));
	    bcm_esw_ces_service_free_tdm(unit, *ces_service);
	    if (!(flags & BCM_CES_WITH_ID))
		bcm_ces_service_free_record(unit, *ces_service);
	    return BCM_E_INTERNAL;
	}

    }

    /*
     * Channel Egress
     */
    if (channelEgressChange || strictDataChange) {
	sal_memset(&ndChannelEgress, 0, sizeof(AgNdMsgConfigChannelEgress));
	ndChannelEgress.n_channel_id    = (AgNdChannel) *ces_service;

	ndChannelEgress.n_channel_id    = (AgNdChannel) *ces_service;
	ndChannelEgress.n_packet_sync_selector = config->egress_channel_config.packet_sync_selector;
	ndChannelEgress.b_rtp_exists    = config->egress_channel_config.rtp_exists;

	if (config->egress_channel_map.slot[0] != 0)
	    config->egress_channel_config.payload_size = config->egress_channel_map.size * 8;
	else if (ctrl->protocol == bcmCesTdmProtocolE1)
	    config->egress_channel_config.payload_size = BCM_CES_E1_PAYLOAD_SIZE;
	else
	    config->egress_channel_config.payload_size = BCM_CES_T1_PAYLOAD_SIZE;

	ndChannelEgress.n_payload_size = config->egress_channel_config.payload_size;
	ndChannelEgress.n_jbf_ring_size = config->egress_channel_config.jbf_ring_size;
	ndChannelEgress.n_jbf_win_size  = config->egress_channel_config.jbf_win_size;
	ndChannelEgress.n_jbf_bop       = config->egress_channel_config.jbf_bop;
	ndChannelEgress.b_drop_on_valid = AG_FALSE;
	ndChannelEgress.n_jbf_addr      = 0x00;

	bcm_ces_copy_header(&record->config.strict_data, &ndChannelEgress.x_strict_data); 
	ndChannelEgress.x_strict_data.e_encapsulation = config->header.encapsulation;

	n_ret = ag_nd_device_write(ctrl->ndHandle, AG_ND_OPCODE_CONFIG_CHANNEL_EGRESS, &ndChannelEgress);
	if (!AG_SUCCEEDED(n_ret))
	{
	    SOC_DEBUG_PRINT((DK_VERBOSE, "AG_ND_OPCODE_CONFIG_CHANNEL_EGRESS failed\n"));
	    bcm_esw_ces_service_free_tdm(unit, *ces_service);
	    if (!(flags & BCM_CES_WITH_ID))
		bcm_ces_service_free_record(unit, *ces_service);
	    return BCM_E_INTERNAL;
	}
    }

    /*
     * Channelizer Ingress
     */
    if (channelizerIngressChange) {
	n_ret = bcm_esw_ces_service_config_channelizer(unit, record, &config->ingress_channel_map, AG_ND_PATH_INGRESS);
	if (n_ret != BCM_E_NONE)
	{
	    SOC_DEBUG_PRINT((DK_VERBOSE, "bcm_esw_ces_service_config_channelizer() failed\n"));
	    bcm_esw_ces_service_free_tdm(unit, *ces_service);
	    if (!(flags & BCM_CES_WITH_ID))
		bcm_ces_service_free_record(unit, *ces_service);
	    return BCM_E_INTERNAL;
	}
    }

    /*
     * Channelizer Egress
     */
    if (channelizerEgressChange) {
	n_ret = bcm_esw_ces_service_config_channelizer(unit, record, &config->egress_channel_map, AG_ND_PATH_EGRESS);
	if (n_ret != BCM_E_NONE)
	{
	    SOC_DEBUG_PRINT((DK_VERBOSE, "bcm_esw_ces_service_config_channelizer() failed\n"));
	    bcm_esw_ces_service_free_tdm(unit, *ces_service);
	    if (!(flags & BCM_CES_WITH_ID))
		bcm_ces_service_free_record(unit, *ces_service);
	    return BCM_E_INTERNAL;
	}
    }

    /*
     * If this is a modification request and the channel 
     * was enabled then it must be reenabled.
     */
    if (enabled)
	bcm_esw_ces_service_enable_set(unit, *ces_service, 1);

	
    /*
     * Mark as configured
     */
    BCM_CES_SET_CONFIGURED(&record->config);
    BCM_CES_SET_CONFIGURED(record->tdmRecord);

    return BCM_E_NONE; 
}

/**
 * Function:
 *      bcm_esw_ces_service_enable_set
 * Purpose:
 *      Enables or disables a CES service.
 * Parameters:
 *      unit - (IN) Unit number.
 *      ces_service - (IN) CES Channel
 *      enable - (IN) Boolean 1==enable, 0==disable
 * Returns:
 *      BCM_E_NONE Operation completed successfully
 *      BCM_E_BADID Invalid service ID was used
 *      BCM_E_INTERNAL An internal error occurred
 * Notes:
 */
int 
bcm_esw_ces_service_enable_set(
    int unit, 
    bcm_ces_service_t ces_service, 
    int enable)
{
    int n_ret;
    bcm_ces_service_record_t *record;
    int i;
    AgNdMsgConfigCircuitEnable     ndCircuitEnable;
    AgNdMsgConfigChannelEnable     ndChannelEnable;
    AgNdMsgConfigRpcMapLabelToChid ndMap;
    AgFramerConfig                 ndFramer;
    bcm_ces_service_global_config_t *ctrl = SOC_CONTROL(unit)->ces_ctrl;
    int16 modPorts[BCM_CES_SLOT_MAX];
    int j;

    /*
     * Find service record.
     */
    n_ret = bcm_ces_service_find(unit, ces_service, &record);
    if (n_ret != BCM_E_NONE)
	return n_ret;

    if (BCM_CES_IS_FREE(&record->config))
	return BCM_E_UNAVAIL;

    memset(modPorts, -1, sizeof(modPorts));

    /*
     * Set enable state for each circuit and framer. The port may have 
     * multiple entries in the map so make sure that only the first one
     * has the enable state changed and ignore the others.
     */
    for (i = record->config.ingress_channel_map.first; i < (record->config.ingress_channel_map.first + record->config.ingress_channel_map.size);i++)
    {
	/*
	 * Has the port state already been modified?
	 */
	j = 0;
	while(modPorts[j] != -1 && j < BCM_CES_SLOT_MAX) {
	    if (modPorts[j] == (int16)record->config.ingress_channel_map.circuit_id[i])
		break;
	    j++;
	}

	if (modPorts[j] != (int16)record->config.ingress_channel_map.circuit_id[i]) {
	    sal_memset(&ndFramer, 0, sizeof(AgFramerConfig));
	    ndFramer.n_circuit_id = (AgNdCircuit)bcm_ces_port_to_circuit_id(unit, record->config.ingress_channel_map.circuit_id[i]);
	    n_ret = ag_nd_device_read(ctrl->ndHandle, AG_ND_OPCODE_FRAMER_PORT_CONFIG, &ndFramer);
	    if (!AG_SUCCEEDED(n_ret)) {
		return BCM_E_INTERNAL;
	    }

	    ndFramer.b_enable = enable;

	    n_ret = ag_nd_device_write(ctrl->ndHandle, AG_ND_OPCODE_FRAMER_PORT_CONFIG, &ndFramer);
	    if (!AG_SUCCEEDED(n_ret))
	    {
		return BCM_E_INTERNAL;
	    }

	    ndCircuitEnable.b_enable = enable;
	    ndCircuitEnable.n_circuit_id = (AgNdCircuit)bcm_ces_port_to_circuit_id(unit, record->config.ingress_channel_map.circuit_id[i]);
	    n_ret = ag_nd_device_write(ctrl->ndHandle, AG_ND_OPCODE_CONFIG_CIRCUIT_ENABLE, &ndCircuitEnable);
	    if (!AG_SUCCEEDED(n_ret))
	    {
		return BCM_E_INTERNAL;
	    }

	    modPorts[j] = (int16)record->config.ingress_channel_map.circuit_id[i];
	}
    }

    sal_memset(&ndMap, 0, sizeof(AgNdMsgConfigRpcMapLabelToChid));
    ndMap.e_encapsulation = record->config.header.encapsulation;
    ndMap.b_enable = enable;
    ndMap.n_channel_id = (AgNdChannel) ces_service;

    /*
     * Set the label from the appropriate field
     */
    switch(ndMap.e_encapsulation)
    {
    case AG_ND_ENCAPSULATION_ETH:
	ndMap.n_label = record->config.header.vc_label;
	break;

    case AG_ND_ENCAPSULATION_IP:
	ndMap.n_label = record->config.header.udp.destination;
	break;

    case AG_ND_ENCAPSULATION_MPLS:
	ndMap.n_label = record->config.header.mpls[0].label;
	break;

    case AG_ND_ENCAPSULATION_L2TP:
	ndMap.n_label = record->config.header.l2tpv3.session_peer_id;
	break;

    default:
	/* Not handled */
    case AG_ND_ENCAPSULATION_PTP_IP:
    case AG_ND_ENCAPSULATION_PTP_EHT:
    case AG_ND_ENCAPSULATION_MAX:
	break;
    }
 

    /*
     * Enable classifier before channels are enabled.
     */
    n_ret = ag_nd_device_write(ctrl->ndHandle, AG_ND_OPCODE_CONFIG_RPC_MAP_LABEL_TO_CHID, &ndMap);
    if (!AG_SUCCEEDED(n_ret))
    {
	return BCM_E_INTERNAL;
    }

    ndChannelEnable.b_enable = enable;
    ndChannelEnable.n_channel_id = (AgNdChannel) ces_service;
    ndChannelEnable.e_path = AG_ND_PATH_EGRESS;
    ndChannelEnable.n_jb_size_milli = 5;

    n_ret = ag_nd_device_write(ctrl->ndHandle, AG_ND_OPCODE_CONFIG_CHANNEL_ENABLE, &ndChannelEnable);
    if (!AG_SUCCEEDED(n_ret))
    {
	return BCM_E_INTERNAL;
    }

    ndChannelEnable.b_enable = enable;
    ndChannelEnable.n_channel_id = (AgNdChannel) ces_service;
    ndChannelEnable.e_path = AG_ND_PATH_INGRESS;
    ndChannelEnable.n_jb_size_milli = 5;

    n_ret = ag_nd_device_write(ctrl->ndHandle, AG_ND_OPCODE_CONFIG_CHANNEL_ENABLE, &ndChannelEnable);
    if (!AG_SUCCEEDED(n_ret))
    {
	return BCM_E_INTERNAL;
    }


    /*
     * Set enabled flag appropriately
     */
    if (enable)
    {
	BCM_CES_SET_ENABLED(&record->config);
	BCM_CES_SET_ENABLED(record->tdmRecord);
    }
    else
    {
	BCM_CES_CLR_ENABLED(&record->config);
	BCM_CES_CLR_ENABLED(record->tdmRecord);
    }

    return BCM_E_NONE; 
}

/**
 * Function:
 *      bcm_esw_ces_service_free_tdm
 * Purpose:
 *      Free TDM resources associated with a service
 * Parameters:
 *      unit - (IN) Unit number.
 *      ces_service - (IN) CES Channel
 * Returns:
 *      BCM_E_xxx
 * Notes:
 */
int 
bcm_esw_ces_service_free_tdm(
    int unit, 
    bcm_ces_service_t ces_service)
{
    bcm_ces_service_record_t *record;
    int n_ret;
    bcm_ces_channel_map_t map;

    /*
     * Find service record.
     */
    n_ret = bcm_ces_service_find(unit, ces_service, &record);
    if (n_ret != BCM_E_NONE)
	return n_ret;

    if (BCM_CES_IS_FREE(&record->config))
	return BCM_E_UNAVAIL;

    /*
     * Free the TDM resources
     */
    if (record->tdmRecord != NULL) {
	/*
	 * Provision channel with no TDM
	 */
	sal_memset(&map, 0, sizeof(bcm_ces_channel_map_t));
	n_ret = bcm_esw_ces_service_config_channelizer(unit, record, &map, AG_ND_PATH_INGRESS);
	n_ret = bcm_esw_ces_service_config_channelizer(unit, record, &map, AG_ND_PATH_EGRESS);

	bcm_esw_port_tdm_delete_service(unit, record->tdmRecord, record);
	record->tdmRecord = NULL;
    }

    return BCM_E_NONE; 
}


/**
 * Function:
 *      bcm_esw_ces_service_destroy
 * Purpose:
 *      Deallocate a CES service.
 * Parameters:
 *      unit - (IN) Unit number.
 *      ces_service - (IN) CES Channel
 * Returns:
 *      BCM_E_NONE Operation completed successfully
 *      BCM_E_BADID Invalid service ID was used
 *      BCM_E_INTERNAL An internal error occurred
 * Notes:
 */
int 
bcm_esw_ces_service_destroy(
    int unit, 
    bcm_ces_service_t ces_service)
{
    bcm_ces_service_record_t *record;
    int n_ret;

    /*
     * Find service record.
     */
    n_ret = bcm_ces_service_find(unit, ces_service, &record);
    if (n_ret != BCM_E_NONE)
	return n_ret;

    if (BCM_CES_IS_FREE(&record->config))
	return BCM_E_UNAVAIL;

    /*
     * Disable
     */
    if (BCM_CES_IS_ENABLED(&record->config)) {
	bcm_esw_ces_service_enable_set(unit, ces_service, 0);
    }

    /*
     * Free the TDM resources
     */
    bcm_esw_ces_service_free_tdm(unit, ces_service);

    /*
     * Reset flags and mark as free
     */
    BCM_CES_CLR_ALL(&record->config);
    BCM_CES_SET_FREE(&record->config);

    return BCM_E_NONE; 
}

/**
 * Function:
 *      bcm_esw_ces_service_destroy_all
 * Purpose:
 *      Deallocate all CES services.
 * Parameters:
 *      unit - (IN) Unit number.
 * Returns:
 *      BCM_E_xxx
 * Notes:
 */
int 
bcm_esw_ces_service_destroy_all(
    int unit)
{
    int i;
    bcm_ces_service_global_config_t *ces_ctrl = (bcm_ces_service_global_config_t *)SOC_CONTROL(unit)->ces_ctrl;

    /*
     * Find all allocated circuits and free them
     */
    for (i = 0;i < BCM_CES_CIRCUIT_IDX_MAX;i++)
    {
	if (ces_ctrl->bcm_ces_service_records[i] != NULL)
	{
	    bcm_esw_ces_service_destroy(unit,  (bcm_ces_service_t)i);
	}
    }

    return BCM_E_NONE;
}


/**
 * Function:
 *      bcm_esw_ces_service_enable_get
 * Purpose:
 *      Get CES service enable state.
 * Parameters:
 *      unit - (IN) Unit number.
 *      ces_service - (IN) CES Channel
 *      enable - (OUT) Boolean 1 == enabled, 0 == disabled
 * Returns:
 *      BCM_E_NONE Operation completed successfully
 *      BCM_E_BADID Invalid service ID was used
 *      BCM_E_INTERNAL An internal error occurred
 * Notes:
 */
int 
bcm_esw_ces_service_enable_get(
    int unit, 
    bcm_ces_service_t ces_service, 
    int *enable)
{
    int n_ret;
    bcm_ces_service_record_t *record;

    /*
     * Find service record.
     */
    n_ret = bcm_ces_service_find(unit, ces_service, &record);
    if (n_ret != BCM_E_NONE)
	return n_ret;

    if (BCM_CES_IS_FREE(&record->config))
	return BCM_E_UNAVAIL;

    *enable = (BCM_CES_IS_ENABLED(&record->config) ? 1:0);
    return BCM_E_NONE; 
}


/**
 * Function:
 *      bcm_esw_ces_service_config_get
 * Purpose:
 *      Get the current configuration parameters for a CES service.
 * Parameters:
 *      unit - (IN) Unit number.
 *      ces_service - (IN) CES Channel
 *      config - (OUT) Service config.
 * Returns:
 *      BCM_E_NONE Operation completed successfully
 *      BCM_E_BADID Invalid service ID was used
 *      BCM_E_INTERNAL An internal error occurred
 * Notes:
 */
int 
bcm_esw_ces_service_config_get(
    int unit, 
    bcm_ces_service_t ces_service, 
    bcm_ces_service_config_t *config)
{
    int n_ret;
    bcm_ces_service_record_t *record;

    /*
     * Find service record.
     */
    n_ret = bcm_ces_service_find(unit, ces_service, &record);
    if (n_ret != BCM_E_NONE)
	return n_ret;

    sal_memcpy(config, &record->config, sizeof(bcm_ces_service_config_t));
    return BCM_E_NONE; 
}

/**
 * Function:
 *      bcm_esw_ces_services_traverse
 * Purpose:
 *      List all currently configured CES services.
 * Parameters:
 *      unit - (IN) Unit number.
 *      cb - (IN) Call back function pointer
 *      user_data - (IN) User data
 * Returns:
 *      BCM_E_NONE Operation completed successfully
 * Notes:
 */
int 
bcm_esw_ces_services_traverse(
    int unit, 
    bcm_ces_service_traverse_cb cb, 
    void *user_data)
{
    int i;
    bcm_ces_service_global_config_t *ces_ctrl = (bcm_ces_service_global_config_t *)SOC_CONTROL(unit)->ces_ctrl;

    for (i = 0;i < BCM_CES_CIRCUIT_IDX_MAX;i++)
    {
	if (ces_ctrl->bcm_ces_service_records[i] != NULL)
	{
	    cb(unit, (void*)&ces_ctrl->bcm_ces_service_records[i]->config);
	}
    }

    return BCM_E_NONE; 
}


/**
 * Function:
 *      bcm_esw_ces_service_ingress_cas_enable_set
 * Purpose:
 *      Enable or disable CAS signalling for a CES service.
 * Parameters:
 *      unit - (IN) Unit number.
 *      ces_service - (IN) CES Channel
 *      enable - (IN) Enable
 * Returns:
 *      BCM_E_xxx
 * Notes:
 */
int 
bcm_esw_ces_service_ingress_cas_enable_set(
    int unit, 
    bcm_ces_service_t ces_service, 
    int enable)
{
    int n_ret;
    bcm_ces_service_record_t *record;
    bcm_ces_service_global_config_t *ctrl = SOC_CONTROL(unit)->ces_ctrl;
    AgNdMsgConfigCasChannelEnable ndCasEnable;

    /*
     * Find service record.
     */
    n_ret = bcm_ces_service_find(unit, ces_service, &record);
    if (n_ret != BCM_E_NONE)
	return n_ret;

    if (BCM_CES_IS_FREE(&record->config))
	return BCM_E_UNAVAIL;

    ndCasEnable.b_enable = (enable ? TRUE:FALSE);
    ndCasEnable.n_channel_id = ces_service;
    n_ret = ag_nd_device_write(ctrl->ndHandle, AG_ND_OPCODE_CONFIG_CAS_CHANNEL_ENABLE, &ndCasEnable);
    if (!AG_SUCCEEDED(n_ret))
    {
	return BCM_E_INTERNAL;
    }

    return BCM_E_NONE; 
}


/**
 * Function:
 *      bcm_esw_ces_service_ingress_cas_enable_get
 * Purpose:
 *      Get the CAS signalling enable status for a CES service.
 * Parameters:
 *      unit - (IN) Unit number.
 *      ces_service - (IN) CES Channel
 *      enable - (OUT) Enable state
 * Returns:
 *      BCM_E_xxx
 * Notes:
 */
int 
bcm_esw_ces_service_ingress_cas_enable_get(
    int unit, 
    bcm_ces_service_t ces_service, 
    int *enable)
{
    int n_ret;
    bcm_ces_service_record_t *record;
    bcm_ces_service_global_config_t *ctrl = SOC_CONTROL(unit)->ces_ctrl;
    AgNdMsgConfigCasChannelEnable ndCasEnable;

    /*
     * Find service record.
     */
    n_ret = bcm_ces_service_find(unit, ces_service, &record);
    if (n_ret != BCM_E_NONE)
	return n_ret;

    if (BCM_CES_IS_FREE(&record->config))
	return BCM_E_UNAVAIL;

    ndCasEnable.n_channel_id = ces_service;
    n_ret = ag_nd_device_read(ctrl->ndHandle, AG_ND_OPCODE_CONFIG_CAS_CHANNEL_ENABLE, &ndCasEnable);
    if (!AG_SUCCEEDED(n_ret))
    {
	return BCM_E_INTERNAL;
    }

    *enable = ndCasEnable.b_enable;
    return BCM_E_NONE; 
}


/**
 * Function:
 *      bcm_esw_ces_service_cas_tx
 * Purpose:
 *      Number of CAS packets to schedule for transmission on a CES
 *      service.
 * Parameters:
 *      unit - (IN) Unit number.
 *      ces_service - (IN) CES Channel
 *      packet_count - (IN) Number of CAS packets to schedule
 * Returns:
 *      BCM_E_xxx
 * Notes:
 */
int 
bcm_esw_ces_service_cas_packet_enable(
    int unit, 
    bcm_ces_service_t ces_service, 
    bcm_ces_cas_packet_control_t *packet_control)
{
    int n_ret;
    bcm_ces_service_record_t *record;
    bcm_ces_service_global_config_t *ctrl = SOC_CONTROL(unit)->ces_ctrl;
    AgNdMsgCommandCasChannelTx ndCasTx;

    /*
     * Find service record.
     */
    n_ret = bcm_ces_service_find(unit, ces_service, &record);
    if (n_ret != BCM_E_NONE)
	return n_ret;

    if (BCM_CES_IS_FREE(&record->config))
	return BCM_E_UNAVAIL;

    ndCasTx.n_channel_id = ces_service;
    ndCasTx.n_packets = packet_control->packet_count;
    n_ret = ag_nd_device_write(ctrl->ndHandle, AG_ND_OPCODE_COMMAND_CAS_CHANNEL_TX, &ndCasTx);
    if (!AG_SUCCEEDED(n_ret))
    {
	return BCM_E_INTERNAL;
    }

    return BCM_E_NONE; 
}


/**
 * Function:
 *      bcm_esw_ces_egress_status_get
 * Purpose:
 *      Gets the CES seervice egress status.
 * Parameters:
 *      unit - (IN) Unit number.
 *      ces_service - (IN) CES Channel
 *      sync_state - (OUT) Sync state
 *      jbf_state - (OUT) Jitter buffer state
 *      ces_cw - (OUT) CES cw
 *      trimming - (OUT) Trimming state
 * Returns:
 *      BCM_E_xxx
 * Notes:
 */
int 
bcm_esw_ces_egress_status_get(
    int unit, 
    bcm_ces_service_t ces_service, 
    bcm_ces_service_egress_status_t *status)
{
    AgResult n_ret;
    AgNdMsgStatusEgress ndStatusEgress;
    bcm_ces_service_record_t *record;
    bcm_ces_service_global_config_t *ctrl = SOC_CONTROL(unit)->ces_ctrl;

    /*
     * Find service record.
     */
    n_ret = bcm_ces_service_find(unit, ces_service, &record);
    if (n_ret != BCM_E_NONE)
	return n_ret;

    if (BCM_CES_IS_FREE(&record->config))
	return BCM_E_UNAVAIL;

    ndStatusEgress.n_channel_id = ces_service;
    n_ret = ag_nd_device_read(ctrl->ndHandle, AG_ND_OPCODE_STATUS_EGRESS, &ndStatusEgress);
    if (!AG_SUCCEEDED(n_ret))
    {
	return BCM_E_INTERNAL;
    }

    status->sync_state = ndStatusEgress.e_sync_state;
    status->jbf_state  = ndStatusEgress.e_jbf_state;
    status->ces_cw     = ndStatusEgress.n_ces_cw;
    status->trimming   = ndStatusEgress.b_trimming;

    return BCM_E_NONE; 
}


/**
 * Function:
 *      bcm_ces_services_cclk_config_set
 * Purpose:
 *      Set the clock configuration parameters that are common to all
 *      CES services.
 * Parameters:
 *      unit - (IN) Unit number.
 *      config - (IN) config
 * Returns:
 *      BCM_E_xxx
 * Notes:
 */
int 
bcm_esw_ces_services_cclk_config_set(
    int unit, 
    bcm_ces_cclk_config_t *config)
{
    AgResult n_ret;
    AgNdMsgConfigNemoCClk ndConfigNemoCClk;
    bcm_ces_service_global_config_t *ctrl = SOC_CONTROL(unit)->ces_ctrl;
#ifdef BCM_CES_CRM_SUPPORT 
    bcm_ces_crm_cclk_config_msg_t crm_msg;
#endif

    config->ref_clk_1_brg = 0; /* Not used */
    config->ref_clk_2_brg = 0; /* Not used */
    config->ext_clk_1_dir = 0; /* Must be zero */
    config->ext_clk_2_dir = 1; /* Must be one */
    config->ref_clk_proto = ctrl->protocol;

    /*
     * Save config
     */
    sal_memcpy(&ctrl->cclk_config, config, sizeof(bcm_ces_cclk_config_t));

    
    

    /*
     * Make message
     */
    sal_memset(&ndConfigNemoCClk, 0, sizeof(AgNdMsgConfigNemoCClk));
    ndConfigNemoCClk.b_cclk_enable = ctrl->cclk_config.cclk_enable;
    ndConfigNemoCClk.e_cclk_select = ctrl->cclk_config.cclk_select;
    ndConfigNemoCClk.e_ref_clk_proto = ctrl->cclk_config.ref_clk_proto;
    ndConfigNemoCClk.e_ref_clk_1_select = ctrl->cclk_config.ref_clk_1_select;
    ndConfigNemoCClk.e_ref_clk_2_select = ctrl->cclk_config.ref_clk_2_select;
    ndConfigNemoCClk.n_ref_clk_1_port = bcm_ces_port_to_circuit_id(unit, ctrl->cclk_config.ref_clk_1_port);
    ndConfigNemoCClk.n_ref_clk_2_port = bcm_ces_port_to_circuit_id(unit, ctrl->cclk_config.ref_clk_2_port);
    ndConfigNemoCClk.n_ref_clk_1_brg = ctrl->cclk_config.ref_clk_1_brg;
    ndConfigNemoCClk.n_ref_clk_2_brg = ctrl->cclk_config.ref_clk_2_brg;
    ndConfigNemoCClk.e_ext_clk_1_dir = ctrl->cclk_config.ext_clk_1_dir;
    ndConfigNemoCClk.e_ext_clk_2_dir = ctrl->cclk_config.ext_clk_2_dir;
    ndConfigNemoCClk.e_ref_clk_1_ptp = AG_ND_PTP_CLK_1;
    ndConfigNemoCClk.e_ref_clk_2_ptp = AG_ND_PTP_CLK_2;

    n_ret = ag_nd_device_write(ctrl->ndHandle, AG_ND_OPCODE_CONFIG_NEMO_CCLK, &ndConfigNemoCClk);
    if (!AG_SUCCEEDED(n_ret))
    {
	return BCM_E_INTERNAL;
    }

    /*
     * Inform CRM of the clock configuration
     */
#ifdef BCM_CES_CRM_SUPPORT 
    sal_memset(&crm_msg, 0, sizeof(bcm_ces_crm_cclk_config_msg_t));
    crm_msg.flags = 0x00;
    crm_msg.e_cclk_select      = ctrl->cclk_config.cclk_select;
    crm_msg.e_ref_clk_1_select = ctrl->cclk_config.ref_clk_1_select;
    crm_msg.e_ref_clk_2_select = ctrl->cclk_config.ref_clk_2_select;
    crm_msg.n_ref_clk_1_port   = ctrl->cclk_config.ref_clk_1_port;
    crm_msg.n_ref_clk_2_port   = ctrl->cclk_config.ref_clk_2_port;

    n_ret =  bcm_esw_ces_crm_dma_msg_send(unit, MOS_MSG_SUBCLASS_CES_CRM_CONFIG, (uint8*)&crm_msg, sizeof(bcm_ces_crm_cclk_config_msg_t));
#else
    n_ret = BCM_E_NONE;
#endif

/*    return n_ret; */
    return BCM_E_NONE;
}




/**
 * Function:
 *      bcm_ces_services_cclk_config_Get
 * Purpose:
 *      Get the clock configuration parameters that are common to all
 *      CES services.
 * Parameters:
 *      unit - (IN) Unit number.
 *      config - (OUT) config
 * Returns:
 *      BCM_E_xxx
 * Notes:
 */
int 
bcm_esw_ces_services_cclk_config_get(
    int unit, 
    bcm_ces_cclk_config_t *config)
{
    bcm_ces_service_global_config_t *ctrl = SOC_CONTROL(unit)->ces_ctrl;

    /*
     * Make config
     */
    sal_memcpy(config, &ctrl->cclk_config, sizeof(bcm_ces_cclk_config_t));

    return BCM_E_NONE; 
}


/**
 * Function:
 *      bcm_ces_service_rclock_config_set
 * Purpose:
 *      Set the rclock timing configuration parameters for a CES
 *      service.
 * Parameters:
 *      unit - (IN) Unit number.
 *      ces_service - (IN) CES Channel
 *      config - (IN) config
 * Returns:
 *      BCM_E_xxx
 * Notes:
 */
int 
bcm_esw_ces_service_rclock_config_set(
    int unit, 
    bcm_ces_service_t ces_service, 
    bcm_ces_rclock_config_t *config)
{
    AgResult n_ret;
    bcm_ces_service_record_t *record;
    bcm_ces_service_global_config_t *ctrl = SOC_CONTROL(unit)->ces_ctrl;
#ifdef BCM_CES_CRM_SUPPORT 
    bcm_ces_crm_rclock_config_msg_t rclock_msg;
#endif
    bcm_ces_rclock_record_t *rclock;

    /*
     * Find rclock instance
     */
    if (config->rclock >= BCM_CES_RCLOCK_MAX)
	return BCM_E_BADID;

    rclock = &ctrl->rclock_record[config->rclock];

    /*
     * Find service record.
     */
    n_ret = bcm_ces_service_find(unit, ces_service, &record);
    if (n_ret != BCM_E_NONE)
	return n_ret;

    /*
     * If there is no TDM associated with the service then there
     * is no service...
     */
    if (record->tdmRecord == NULL)
	return BCM_E_INTERNAL;

    /*
     * If the new service is different to the old service then
     * disconnect the old before adding the new.
     */
    if (record != rclock->service && rclock->service != NULL) {
	rclock->service->rclock = NULL;
    }

    rclock->service = record;
    record->rclock = rclock;

    /*
     * Save a copy
     */
    sal_memcpy(&rclock->config, config, sizeof(bcm_ces_rclock_config_t));

    /*
     * Inform CRM of the clock configuration
     */
#ifdef BCM_CES_CRM_SUPPORT 
    sal_memset(&rclock_msg, 0, sizeof(bcm_ces_crm_rclock_config_msg_t));
    rclock_msg.flags         = 0x00;
    rclock_msg.b_enable      = config->enable;
    rclock_msg.e_protocol    = ctrl->protocol;
    rclock_msg.b_structured  = record->tdmRecord->config.b_structured;
    rclock_msg.output_brg    = config->output_brg;
    rclock_msg.rclock        = config->rclock;
    rclock_msg.port          = bcm_ces_port_to_circuit_id(unit, config->port);
    rclock_msg.recovery_type = config->recovery_type;
    rclock_msg.service       = ces_service;

    n_ret =  bcm_esw_ces_crm_dma_msg_send(unit, MOS_MSG_SUBCLASS_CES_CRM_RCLOCK_CONFIG, (uint8*)&rclock_msg, sizeof(bcm_ces_crm_rclock_config_msg_t));
#else
    n_ret = BCM_E_NONE;
#endif

    return BCM_E_NONE;  
}


/*
 * Function:
 *      bcm_ces_service_rclock_config_get
 * Purpose:
 *      Get the rclock configuration parameters for a CES
 *      service.
 * Parameters:
 *      unit - (IN) Unit number.
 *      ces_service - (IN) CES Channel
 *      config - (OUT) config
 * Returns:
 *      BCM_E_xxx
 * Notes:
 */
int 
bcm_esw_ces_service_rclock_config_get(
    int unit, 
    bcm_ces_service_t ces_service, 
    bcm_ces_rclock_config_t *config)
{
    AgResult n_ret;
    bcm_ces_service_record_t *record;
    bcm_ces_rclock_record_t *rclock;
    bcm_ces_service_global_config_t *ctrl = SOC_CONTROL(unit)->ces_ctrl;

    /*
     * Find service record.
     */
    n_ret = bcm_ces_service_find(unit, ces_service, &record);
    if (n_ret != BCM_E_NONE)
	return n_ret;

    if (BCM_CES_IS_FREE(&record->config))
	return BCM_E_UNAVAIL;

    /*
     * If there is no valid rclock associated then return an error
     */
    if (record->rclock == NULL)
	return BCM_E_INTERNAL;

    /*
     * Find rclock instance
     */
    if (config->rclock >= BCM_CES_RCLOCK_MAX)
	return BCM_E_BADID;

    rclock = &ctrl->rclock_record[config->rclock];

    /*
     * Save a copy
     */
    sal_memcpy(config, &rclock->config, sizeof(bcm_ces_rclock_config_t));

    return BCM_E_NONE; 
}


/**
 * Function:
 *      bcm_ces_diag_get()
 * Purpose:
 *      
 *      
 * Parameters:
 *      unit - (IN) Unit number.
 *      config - (OUT) config
 * Returns:
 *      BCM_E_xxx
 * Notes:
 */
int 
bcm_ces_diag_get(
    int unit, 
    AgNdMsgDiag *config)
{
    AgResult n_ret;
    bcm_ces_service_global_config_t *ctrl = SOC_CONTROL(unit)->ces_ctrl;

    n_ret = ag_nd_device_read(ctrl->ndHandle, AG_ND_OPCODE_DIAG, config);
    if (!AG_SUCCEEDED(n_ret))
    {
	return BCM_E_INTERNAL;
    }

    return BCM_E_NONE; 
}


/**
 * Function:
 *      bcm_ces_diag_set()
 * Purpose:
 *      
 *      
 * Parameters:
 *      unit - (IN) Unit number.
 *      config - (OUT) config
 * Returns:
 *      BCM_E_xxx
 * Notes:
 */
int 
bcm_ces_diag_set(
    int unit, 
    AgNdMsgDiag *config)
{
    AgResult n_ret;
    bcm_ces_service_global_config_t *ctrl = SOC_CONTROL(unit)->ces_ctrl;

    n_ret = ag_nd_device_write(ctrl->ndHandle, AG_ND_OPCODE_DIAG, config);
    if (!AG_SUCCEEDED(n_ret))
    {
	return BCM_E_INTERNAL;
    }

    return BCM_E_NONE; 
}

/**
 * Function:
 *      bcm_esw_ces_attach_ethernet()
 * Purpose:
 *      
 *      
 * Parameters:
 *      unit - (IN) Unit number.
 *      config - (OUT) config
 * Returns:
 *      BCM_E_xxx
 * Notes:
 */
int 
bcm_esw_ces_attach_ethernet(int unit, int port) {

    

    /*
     * Enable CES MII and attach to pipeline
     */

    return BCM_E_NONE;
}

/**
 * Function:
 *      bcm_esw_ces_ethernet_config_set
 * Purpose:
 *      
 *      
 * Parameters:
 *      unit - (IN) Unit number.
 *      port - (IN) Port number
 *      config - (IN) config
 * Returns:
 *      BCM_E_xxx
 * Notes:
 */
int 
bcm_esw_ces_ethernet_config_set(int unit, int port, bcm_ces_mac_cmd_config_t *config) {
    AgResult n_ret;
    bcm_ces_service_global_config_t *ctrl = SOC_CONTROL(unit)->ces_ctrl;

    ctrl->ndMac.x_command_config.b_rx_error_discard_enable          = config->b_rx_error_discard_enable;
    ctrl->ndMac.x_command_config.b_no_length_check                  = config->b_no_length_check;      
    ctrl->ndMac.x_command_config.b_control_frame_enable             = config->b_control_frame_enable; 
    ctrl->ndMac.x_command_config.b_node_wake_up_request_indication  = config->b_node_wake_up_request_indication;
    ctrl->ndMac.x_command_config.b_put_core_in_sleep_mode           = config->b_put_core_in_sleep_mode;       
    ctrl->ndMac.x_command_config.b_enable_magic_packet_detection    = config->b_enable_magic_packet_detection;
    ctrl->ndMac.x_command_config.b_software_reset                   = config->b_software_reset;               
    ctrl->ndMac.x_command_config.b_is_late_collision_condition      = config->b_is_late_collision_condition;  
    ctrl->ndMac.x_command_config.b_is_excessive_collision_condition = config->b_is_excessive_collision_condition; 
    ctrl->ndMac.x_command_config.b_enable_half_duplex               = config->b_enable_half_duplex;           
    ctrl->ndMac.x_command_config.b_insert_mac_addr_on_transmit      = config->b_insert_mac_addr_on_transmit;  
    ctrl->ndMac.x_command_config.b_ignore_pause_frame_quanta        = config->b_ignore_pause_frame_quanta;    
    ctrl->ndMac.x_command_config.b_fwd_pause_frames                 = config->b_fwd_pause_frames;             
    ctrl->ndMac.x_command_config.b_fwd_crc_field                    = config->b_fwd_crc_field;                
    ctrl->ndMac.x_command_config.b_enable_frame_padding             = config->b_enable_frame_padding;         
    ctrl->ndMac.x_command_config.b_enable_promiscuous_mode          = config->b_enable_promiscuous_mode;      
    ctrl->ndMac.x_command_config.b_enable_mac_receive               = config->b_enable_mac_receive;           
    ctrl->ndMac.x_command_config.b_enable_mac_transmit              = config->b_enable_mac_transmit; 

    n_ret = ag_nd_device_write(ctrl->ndHandle, AG_ND_OPCODE_CONFIG_MAC, &ctrl->ndMac);
    if (!AG_SUCCEEDED(n_ret))
    {
	return BCM_E_INTERNAL;
    }

    return BCM_E_NONE;
}

/**
 * Function:
 *      bcm_esw_ces_ethernet_config_get
 * Purpose:
 *      
 *      
 * Parameters:
 *      unit - (IN) Unit number.
 *      port - (IN) Port number
 *      config - (OUT) config
 * Returns:
 *      BCM_E_xxx
 * Notes:
 */
int 
bcm_esw_ces_ethernet_config_get(int unit, int port, bcm_ces_mac_cmd_config_t *config) {
    bcm_ces_service_global_config_t *ctrl = SOC_CONTROL(unit)->ces_ctrl;

    config->b_rx_error_discard_enable          = ctrl->ndMac.x_command_config.b_rx_error_discard_enable;  
    config->b_no_length_check                  = ctrl->ndMac.x_command_config.b_no_length_check;      
    config->b_control_frame_enable             = ctrl->ndMac.x_command_config.b_control_frame_enable; 
    config->b_node_wake_up_request_indication  = ctrl->ndMac.x_command_config.b_node_wake_up_request_indication;
    config->b_put_core_in_sleep_mode           = ctrl->ndMac.x_command_config.b_put_core_in_sleep_mode;       
    config->b_enable_magic_packet_detection    = ctrl->ndMac.x_command_config.b_enable_magic_packet_detection;
    config->b_software_reset                   = ctrl->ndMac.x_command_config.b_software_reset;               
    config->b_is_late_collision_condition      = ctrl->ndMac.x_command_config.b_is_late_collision_condition;  
    config->b_is_excessive_collision_condition = ctrl->ndMac.x_command_config.b_is_excessive_collision_condition; 
    config->b_enable_half_duplex               = ctrl->ndMac.x_command_config.b_enable_half_duplex;           
    config->b_insert_mac_addr_on_transmit      = ctrl->ndMac.x_command_config.b_insert_mac_addr_on_transmit;  
    config->b_ignore_pause_frame_quanta        = ctrl->ndMac.x_command_config.b_ignore_pause_frame_quanta;    
    config->b_fwd_pause_frames                 = ctrl->ndMac.x_command_config.b_fwd_pause_frames;             
    config->b_fwd_crc_field                    = ctrl->ndMac.x_command_config.b_fwd_crc_field;                
    config->b_enable_frame_padding             = ctrl->ndMac.x_command_config.b_enable_frame_padding;         
    config->b_enable_promiscuous_mode          = ctrl->ndMac.x_command_config.b_enable_promiscuous_mode;      
    config->b_enable_mac_receive               = ctrl->ndMac.x_command_config.b_enable_mac_receive;           
    config->b_enable_mac_transmit              = ctrl->ndMac.x_command_config.b_enable_mac_transmit; 

    return BCM_E_NONE;
}

/**
 * Function:
 *      bcm_esw_ces_mac_pm_get
 * Purpose:
 *      
 *      
 * Parameters:
 *      unit - (IN) Unit number.
 *      port - (IN) Port number
 *      config - (IN) config
 * Returns:
 *      BCM_E_xxx
 * Notes:
 */
int 
bcm_esw_ces_mac_pm_get(int unit, AgNdMsgPmMac *stats) {
    AgResult n_ret;
    bcm_ces_service_global_config_t *ctrl = SOC_CONTROL(unit)->ces_ctrl;


    n_ret = ag_nd_device_read(ctrl->ndHandle, AG_ND_OPCODE_PM_MAC, stats);
    if (!AG_SUCCEEDED(n_ret))
    {
	return BCM_E_INTERNAL;
    }

    return BCM_E_NONE;
}

/**
 * Function:
 *      bcm_esw_ces_rpc_pm_set
 * Purpose:
 *      
 *      
 * Parameters:
 *      unit   - (IN) Unit number.
 *      global - (IN) Global PM stats config struct
 *      
 * Returns:
 *      BCM_E_xxx
 * Notes:
 */
int 
bcm_esw_ces_rpc_pm_set(int unit, AgNdMsgConfigRpcPolicy *policy) {
    AgResult n_ret;
    bcm_ces_service_global_config_t *ctrl = SOC_CONTROL(unit)->ces_ctrl;

    /*
     * Set RPC global PM count registers
     */
    n_ret = ag_nd_device_write(ctrl->ndHandle, AG_ND_OPCODE_CONFIG_RPC_POLICY, policy);
    if (!AG_SUCCEEDED(n_ret))
    {
	return BCM_E_INTERNAL;
    }

    return BCM_E_NONE;
}


/**
 * Function:
 *      bcm_esw_ces_rpc_pm_get
 * Purpose:
 *      
 *      
 * Parameters:
 *      unit   - (IN) Unit number.
 *      global - (IN) Global PM stats struct
 *      
 * Returns:
 *      BCM_E_xxx
 * Notes:
 */
int 
bcm_esw_ces_rpc_pm_get(int unit, 
		       AgNdMsgConfigRpcPolicy *policy,
		       AgNdMsgPmGlobal *global) {
    AgResult n_ret;
    bcm_ces_service_global_config_t *ctrl = SOC_CONTROL(unit)->ces_ctrl;

    /*
     * Read global count registers
     */
    n_ret = ag_nd_device_read(ctrl->ndHandle, AG_ND_OPCODE_PM_GLOBAL, global);
    if (!AG_SUCCEEDED(n_ret))
    {
	return BCM_E_INTERNAL;
    }

    /*
     * Read policy
     */
    n_ret = ag_nd_device_read(ctrl->ndHandle, AG_ND_OPCODE_CONFIG_RPC_POLICY, policy);
    if (!AG_SUCCEEDED(n_ret))
    {
	return BCM_E_INTERNAL;
    }


    return BCM_E_NONE;
}

/**
 * Function:
 *      bcm_esw_ces_service_pm_get
 * Purpose:
 *      
 *      
 * Parameters:
 *      unit   - (IN) Unit number.
 *      global - (IN) Global PM stats struct
 *      
 * Returns:
 *      BCM_E_xxx
 * Notes:
 */
int 
bcm_esw_ces_service_pm_get(int unit, bcm_ces_service_t ces_service, bcm_ces_service_pm_stats_t *pm_counts) {
    AgResult n_ret;
    bcm_ces_service_record_t *record;

    /*
     * Find service record.
     */
    n_ret = bcm_ces_service_find(unit, ces_service, &record);
    if (n_ret != BCM_E_NONE)
	return n_ret;

    if (BCM_CES_IS_FREE(&record->config))
	return BCM_E_UNAVAIL;

    sal_memcpy(pm_counts, &record->pm_counts, sizeof(bcm_ces_service_pm_stats_t));
    return BCM_E_NONE;
}

/**
 * Function:
 *      bcm_esw_ces_service_pm_clear
 * Purpose:
 *      
 *      
 * Parameters:
 *      unit   - (IN) Unit number.
 *      global - (IN) Global PM stats struct
 *      
 * Returns:
 *      BCM_E_xxx
 * Notes:
 */
int 
bcm_esw_ces_service_pm_clear(int unit, bcm_ces_service_t ces_service) {

    AgResult n_ret;
    bcm_ces_service_record_t *record;

    /*
     * Find service record.
     */
    n_ret = bcm_ces_service_find(unit, ces_service, &record);
    if (n_ret != BCM_E_NONE)
	return n_ret;

    if (BCM_CES_IS_FREE(&record->config))
	return BCM_E_UNAVAIL;

    sal_memset(&record->pm_counts, 0, sizeof(bcm_ces_service_pm_stats_t));
    return BCM_E_NONE;
}

/**
 * Function:
 *      bcm_esw_ces_framer_prbs_set
 * Purpose:
 *      
 *      
 * Parameters:
 *      unit   - (IN) Unit number.
 *      global - (IN) Global PM stats struct
 *      
 * Returns:
 *      BCM_E_xxx
 * Notes:
 */
int
bcm_esw_ces_framer_prbs_set(int unit, int port, int dir, int mode, int start, int end) {
    uint32 rval;
    int batm_port = bcm_ces_port_to_circuit_id(unit, port);
    int i;
    uint32 ts = 0;

    /*
     * Setup timeslot selection
     */
    if (start != 0) {
	for (i = start; i <= end;i++)
	    ts |= (1 << i);
    }

    if (batm_port < 8) {
	/*
	 * Setup the Framer PRBS
	 */
	READ_FPRBCFG1r(unit, &rval);
	soc_reg_field_set(unit, FPRBCFG1r, &rval, BATM_DIRf, dir);
	soc_reg_field_set(unit, FPRBCFG1r, &rval, BATM_PRBMSf, mode);
	soc_reg_field_set(unit, FPRBCFG1r, &rval, BATM_PORTSELf, batm_port);
	if (start == 0) {
	    soc_reg_field_set(unit, FPRBCFG1r, &rval, BATM_UFPf, 1);
	    soc_reg_field_set(unit, FPRBCFG1r, &rval, BATM_TSGf, 0);
	} else {
	    soc_reg_field_set(unit, FPRBCFG1r, &rval, BATM_UFPf, 0);
	    soc_reg_field_set(unit, FPRBCFG1r, &rval, BATM_TSGf, 1);
	}
	WRITE_FPRBCFG1r(unit, rval);

	/*
	 * Setup timeslot selection
	 */
	if (start != 0) {
	    WRITE_FRPRTSEL1_1r(unit, ts & 0xFFFF);
	    WRITE_FRPRTSEL1_2r(unit, (ts >> 16) & 0xFFFF);
	}
    } else {
	/*
	 * Setup the Framer PRBS
	 */
	READ_FPRBCFG2r(unit, &rval);
	soc_reg_field_set(unit, FPRBCFG2r, &rval, BATM_DIRf, dir);
	soc_reg_field_set(unit, FPRBCFG2r, &rval, BATM_PRBMSf, mode);
	soc_reg_field_set(unit, FPRBCFG2r, &rval, BATM_PORTSELf, batm_port % 8);
	if (start == 0) {
	    soc_reg_field_set(unit, FPRBCFG2r, &rval, BATM_UFPf, 1);
	    soc_reg_field_set(unit, FPRBCFG2r, &rval, BATM_TSGf, 0);
	} else {
	    soc_reg_field_set(unit, FPRBCFG2r, &rval, BATM_UFPf, 0);
	    soc_reg_field_set(unit, FPRBCFG2r, &rval, BATM_TSGf, 1);
	}
	WRITE_FPRBCFG2r(unit, rval);

        /*
	 * Setup timeslot selection
	 */
	if (start != 0) {
	    WRITE_FRPRTSEL2_1r(unit, ts & 0xFFFF);
	    WRITE_FRPRTSEL2_2r(unit, (ts >> 16) & 0xFFFF);
	}
    }

    return BCM_E_NONE;
}

/**
 * Function:
 *      bcm_esw_ces_framer_prbs_status
 * Purpose:
 *      
 *      
 * Parameters:
 *      unit   - (IN) Unit number.
 *      global - (IN) Global PM stats struct
 *      
 * Returns:
 *      BCM_E_xxx
 * Notes:
 */
int
bcm_esw_ces_framer_prbs_status(int unit, int port, int *status) {
    uint32 rval;
    int batm_port = bcm_ces_port_to_circuit_id(unit, port);

    if (batm_port < 8) {
	/*
	 * Get status
	 */
	READ_FRPRBSTAT1r(unit, &rval);
    } else {
	READ_FRPRBSTAT2r(unit, &rval);
    }

    *status = rval & 0x1FF;

    return BCM_E_NONE;
}

#else
int __no_complilation_complaints_about_ces__0;
#endif /* INCLUDE_CES */
